From 931a7dc1ac0dbc7e6c1083583ced915e572f5b47 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sat, 2 Mar 2019 12:32:44 +0300 Subject: all protocols (even virtual ones) moved to the Protocols folder --- protocols/NewsAggregator/NewsAggregator.vcxproj | 28 + .../NewsAggregator/NewsAggregator.vcxproj.filters | 4 + protocols/NewsAggregator/Res/AddFeed.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/CheckALL.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/Disabled.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/Enabled.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/Export.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/Import.ico | Bin 0 -> 1150 bytes protocols/NewsAggregator/Res/Main.ico | Bin 0 -> 5430 bytes protocols/NewsAggregator/Res/Resource.rc | 184 ++++ protocols/NewsAggregator/Res/Version.rc | 9 + protocols/NewsAggregator/Src/Authentication.cpp | 101 ++ protocols/NewsAggregator/Src/CheckFeed.cpp | 473 +++++++++ protocols/NewsAggregator/Src/Icons.cpp | 53 + protocols/NewsAggregator/Src/Menus.cpp | 83 ++ protocols/NewsAggregator/Src/NewsAggregator.cpp | 106 ++ protocols/NewsAggregator/Src/Options.cpp | 1016 ++++++++++++++++++++ protocols/NewsAggregator/Src/Options.h | 162 ++++ protocols/NewsAggregator/Src/Services.cpp | 261 +++++ protocols/NewsAggregator/Src/Update.cpp | 139 +++ protocols/NewsAggregator/Src/Utils.cpp | 445 +++++++++ protocols/NewsAggregator/Src/resource.h | 57 ++ protocols/NewsAggregator/Src/stdafx.cxx | 18 + protocols/NewsAggregator/Src/stdafx.h | 167 ++++ protocols/NewsAggregator/Src/version.h | 13 + protocols/NewsAggregator/docs/AtomText.txt | 482 ++++++++++ protocols/NewsAggregator/docs/RssText.txt | 713 ++++++++++++++ protocols/NewsAggregator/docs/ToDo.txt | 11 + .../NewsAggregator/docs/subscriptions_test.xml | 169 ++++ .../Proto_NewsAggregator.vcxproj | 28 + .../Proto_NewsAggregator.vcxproj.filters | 4 + .../proto_newsaggregator/res/Offline.ico | Bin 0 -> 5430 bytes .../proto_newsaggregator/res/Online.ico | Bin 0 -> 5430 bytes .../res/Proto_NewsAggregator.rc | 70 ++ .../proto_newsaggregator/src/resource.h | 17 + 35 files changed, 4813 insertions(+) create mode 100644 protocols/NewsAggregator/NewsAggregator.vcxproj create mode 100644 protocols/NewsAggregator/NewsAggregator.vcxproj.filters create mode 100644 protocols/NewsAggregator/Res/AddFeed.ico create mode 100644 protocols/NewsAggregator/Res/CheckALL.ico create mode 100644 protocols/NewsAggregator/Res/Disabled.ico create mode 100644 protocols/NewsAggregator/Res/Enabled.ico create mode 100644 protocols/NewsAggregator/Res/Export.ico create mode 100644 protocols/NewsAggregator/Res/Import.ico create mode 100644 protocols/NewsAggregator/Res/Main.ico create mode 100644 protocols/NewsAggregator/Res/Resource.rc create mode 100644 protocols/NewsAggregator/Res/Version.rc create mode 100644 protocols/NewsAggregator/Src/Authentication.cpp create mode 100644 protocols/NewsAggregator/Src/CheckFeed.cpp create mode 100644 protocols/NewsAggregator/Src/Icons.cpp create mode 100644 protocols/NewsAggregator/Src/Menus.cpp create mode 100644 protocols/NewsAggregator/Src/NewsAggregator.cpp create mode 100644 protocols/NewsAggregator/Src/Options.cpp create mode 100644 protocols/NewsAggregator/Src/Options.h create mode 100644 protocols/NewsAggregator/Src/Services.cpp create mode 100644 protocols/NewsAggregator/Src/Update.cpp create mode 100644 protocols/NewsAggregator/Src/Utils.cpp create mode 100644 protocols/NewsAggregator/Src/resource.h create mode 100644 protocols/NewsAggregator/Src/stdafx.cxx create mode 100644 protocols/NewsAggregator/Src/stdafx.h create mode 100644 protocols/NewsAggregator/Src/version.h create mode 100644 protocols/NewsAggregator/docs/AtomText.txt create mode 100644 protocols/NewsAggregator/docs/RssText.txt create mode 100644 protocols/NewsAggregator/docs/ToDo.txt create mode 100644 protocols/NewsAggregator/docs/subscriptions_test.xml create mode 100644 protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj create mode 100644 protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj.filters create mode 100644 protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico create mode 100644 protocols/NewsAggregator/proto_newsaggregator/res/Online.ico create mode 100644 protocols/NewsAggregator/proto_newsaggregator/res/Proto_NewsAggregator.rc create mode 100644 protocols/NewsAggregator/proto_newsaggregator/src/resource.h (limited to 'protocols/NewsAggregator') diff --git a/protocols/NewsAggregator/NewsAggregator.vcxproj b/protocols/NewsAggregator/NewsAggregator.vcxproj new file mode 100644 index 0000000000..3b427aec7d --- /dev/null +++ b/protocols/NewsAggregator/NewsAggregator.vcxproj @@ -0,0 +1,28 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + NewsAggregator + {6DE11A47-2268-4B08-8DE5-15A1705FCE28} + + + + + \ No newline at end of file diff --git a/protocols/NewsAggregator/NewsAggregator.vcxproj.filters b/protocols/NewsAggregator/NewsAggregator.vcxproj.filters new file mode 100644 index 0000000000..5ef6c526f1 --- /dev/null +++ b/protocols/NewsAggregator/NewsAggregator.vcxproj.filters @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/protocols/NewsAggregator/Res/AddFeed.ico b/protocols/NewsAggregator/Res/AddFeed.ico new file mode 100644 index 0000000000..6a7e9af31e Binary files /dev/null and b/protocols/NewsAggregator/Res/AddFeed.ico differ diff --git a/protocols/NewsAggregator/Res/CheckALL.ico b/protocols/NewsAggregator/Res/CheckALL.ico new file mode 100644 index 0000000000..9a720c2e07 Binary files /dev/null and b/protocols/NewsAggregator/Res/CheckALL.ico differ diff --git a/protocols/NewsAggregator/Res/Disabled.ico b/protocols/NewsAggregator/Res/Disabled.ico new file mode 100644 index 0000000000..1055879373 Binary files /dev/null and b/protocols/NewsAggregator/Res/Disabled.ico differ diff --git a/protocols/NewsAggregator/Res/Enabled.ico b/protocols/NewsAggregator/Res/Enabled.ico new file mode 100644 index 0000000000..b62ed83376 Binary files /dev/null and b/protocols/NewsAggregator/Res/Enabled.ico differ diff --git a/protocols/NewsAggregator/Res/Export.ico b/protocols/NewsAggregator/Res/Export.ico new file mode 100644 index 0000000000..945815b772 Binary files /dev/null and b/protocols/NewsAggregator/Res/Export.ico differ diff --git a/protocols/NewsAggregator/Res/Import.ico b/protocols/NewsAggregator/Res/Import.ico new file mode 100644 index 0000000000..bbe29ca82f Binary files /dev/null and b/protocols/NewsAggregator/Res/Import.ico differ diff --git a/protocols/NewsAggregator/Res/Main.ico b/protocols/NewsAggregator/Res/Main.ico new file mode 100644 index 0000000000..75c9ff1aa3 Binary files /dev/null and b/protocols/NewsAggregator/Res/Main.ico differ diff --git a/protocols/NewsAggregator/Res/Resource.rc b/protocols/NewsAggregator/Res/Resource.rc new file mode 100644 index 0000000000..eb7688640f --- /dev/null +++ b/protocols/NewsAggregator/Res/Resource.rc @@ -0,0 +1,184 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\src\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian (Russia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT +#pragma code_page(1251) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON "Main.ico" +IDI_CHECKALL ICON "CheckAll.ico" +IDI_ADDFEED ICON "AddFeed.ico" +IDI_IMPORTFEEDS ICON "Import.ico" +IDI_EXPORTFEEDS ICON "Export.ico" +IDI_ENABLED ICON "Enabled.ico" +IDI_DISABLED ICON "Disabled.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTIONS DIALOGEX 0, 0, 314, 234 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_FEEDLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,9,8,294,174 + PUSHBUTTON "Add",IDC_ADD,79,187,50,14 + PUSHBUTTON "Change",IDC_CHANGE,133,187,50,14 + PUSHBUTTON "Remove",IDC_REMOVE,187,187,50,14 + PUSHBUTTON "Import",IDC_IMPORT,252,204,50,14 + PUSHBUTTON "Export",IDC_EXPORT,252,219,50,14 + CONTROL "Retrieve news at startup",IDC_STARTUPRETRIEVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,205,203,10 +END + +IDD_ADDFEED DIALOGEX 0, 0, 250, 228 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,140,209,50,14 + PUSHBUTTON "Cancel",IDCANCEL,194,209,50,14 + GROUPBOX "General Settings",IDC_STATIC,6,3,238,61 + RTEXT "Title",IDC_STATIC,17,16,44,8 + EDITTEXT IDC_FEEDTITLE,63,14,173,13,ES_AUTOHSCROLL + RTEXT "URL",IDC_STATIC,17,31,44,8 + EDITTEXT IDC_FEEDURL,63,29,173,13,ES_AUTOHSCROLL + RTEXT "Check every",IDC_STATIC,7,46,54,8 + LTEXT "minutes",IDC_STATIC,100,46,75,8 + PUSHBUTTON "Check Feed",IDC_DISCOVERY,177,44,59,12 + GROUPBOX "Authentication",IDC_STATIC,6,67,238,48 + CONTROL "Use &authentication",IDC_USEAUTH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,80,157,10 + RTEXT "Login",IDC_STATIC,16,96,42,8 + EDITTEXT IDC_LOGIN,61,94,63,14,ES_AUTOHSCROLL | WS_DISABLED + RTEXT "Password",IDC_STATIC,129,96,41,8 + EDITTEXT IDC_PASSWORD,173,94,63,14,ES_PASSWORD | ES_AUTOHSCROLL | WS_DISABLED + GROUPBOX "Visualization",IDC_STATIC,6,119,238,85 + LTEXT "Display news using the following format:",IDC_STATIC,13,132,168,8 + EDITTEXT IDC_TAGSEDIT,13,143,224,36,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "All item's tags are valid. Put them between #. Example: ##",IDC_STATIC,14,182,160,16 + PUSHBUTTON "Reset",IDC_RESET,192,184,45,14 + PUSHBUTTON "?",IDC_TAGHELP,175,184,15,14 + LTEXT "0 - check manually",IDC_STATIC,100,55,78,8 + EDITTEXT IDC_CHECKTIME,64,45,32,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_TIMEOUT_VALUE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,85,45,11,12 +END + +IDD_AUTHENTICATION DIALOGEX 0, 0, 211, 106 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Authentication" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CTEXT "Feed name",IDC_FEEDNAME,7,5,196,8 + LTEXT "This feed seems to need authentication. Please fill username and password fields:",IDC_STATIC,7,19,196,22 + RTEXT "Username",IDC_STATIC,25,49,42,8 + EDITTEXT IDC_FEEDUSERNAME,71,46,80,14,ES_AUTOHSCROLL + RTEXT "Password",IDC_STATIC,25,66,42,8 + EDITTEXT IDC_FEEDPASSWORD,71,63,80,14,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,98,87,50,14 + PUSHBUTTON "Cancel",IDCANCEL,153,87,50,14 +END + +IDD_FEEDEXPORT DIALOGEX 0, 0, 275, 138 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT +CAPTION "Export" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Available feeds:",IDC_STATIC,6,6,100,8 + LTEXT "Feeds to be exported:",IDC_STATIC,150,6,98,8 + LISTBOX IDC_FEEDSLIST,6,18,120,98,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + LISTBOX IDC_FEEDSEXPORTLIST,150,18,120,98,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + PUSHBUTTON "->",IDC_ADDFEED,129,24,17,14 + PUSHBUTTON "<-",IDC_REMOVEFEED,129,40,17,14 + PUSHBUTTON "&Export",IDOK,164,120,50,14 + PUSHBUTTON "&Close",IDCANCEL,220,120,50,14 + PUSHBUTTON "->>",IDC_ADDALLFEEDS,129,78,17,14 + PUSHBUTTON "<<-",IDC_REMOVEALLFEEDS,129,95,17,14 +END + +IDD_FEEDIMPORT DIALOGEX 0, 0, 276, 161 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT +CAPTION "Import" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "Import from:",IDC_STATIC,6,7,47,8 + EDITTEXT IDC_IMPORTFILEPATH,59,5,186,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "...",IDC_BROWSEIMPORTFILE,248,5,22,14 + LTEXT "Available feeds:",IDC_STATIC,6,26,100,8 + LTEXT "Feeds to be imported:",IDC_STATIC,150,26,101,8 + LISTBOX IDC_FEEDSLIST,6,38,120,98,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + LISTBOX IDC_FEEDSIMPORTLIST,150,38,120,98,LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + PUSHBUTTON "->",IDC_ADDFEED,129,47,17,14 + PUSHBUTTON "<-",IDC_REMOVEFEED,129,63,17,14 + PUSHBUTTON "&Import",IDOK,165,141,50,14 + PUSHBUTTON "&Close",IDCANCEL,220,141,50,14 + PUSHBUTTON "->>",IDC_ADDALLFEEDS,129,97,17,14 + PUSHBUTTON "<<-",IDC_REMOVEALLFEEDS,129,113,17,14 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\src\\resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +#endif // Russian (Russia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/protocols/NewsAggregator/Res/Version.rc b/protocols/NewsAggregator/Res/Version.rc new file mode 100644 index 0000000000..5a5ddd63ed --- /dev/null +++ b/protocols/NewsAggregator/Res/Version.rc @@ -0,0 +1,9 @@ +// Microsoft Visual C++ generated resource script. +// +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include "..\src\version.h" + +#include "..\..\build\Version.rc" 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"##", 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" diff --git a/protocols/NewsAggregator/docs/AtomText.txt b/protocols/NewsAggregator/docs/AtomText.txt new file mode 100644 index 0000000000..f6d2723822 --- /dev/null +++ b/protocols/NewsAggregator/docs/AtomText.txt @@ -0,0 +1,482 @@ +<?xml version="1.0"?> + +<feed xmlns="http://www.w3.org/2005/Atom"> + <updated>2012-01-04T06:45:38Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic</id> + + <title>watcher-miranda project updates - Google Code + + + + + + 2012-01-04T06:45:38Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12pftbjzm31hlgp204cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-0.10.latest-vc2010_x64-static.7z">miranda-0.10.latest-vc2010_x64-static.7z</a> (Miranda IM 0.10.0 Alpha #2 VC2010 x64) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2012-01-03T21:06:03Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12qdzfqdumlchjgm04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-v0100a2w11.7z">miranda-v0100a2w11.7z</a> (T - ) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2012-01-03T20:47:25Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z135ypx45kehgtcq304cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-v0100a2w11.7z">miranda-v0100a2w11.7z</a> (Miranda IM 0.10.0 Alpha #2 VC6111) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2012-01-03T09:21:55Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12szb1zzybuejnnv22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=mirotr1.7z">mirotr1.7z</a> (Miranda IM 0.10.0 Alpha #2 VC6) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2012-01-02T20:12:28Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12ltxpzuvr4xvahk04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda32.exe">miranda32.exe</a> (Miranda32 v.0.9.40.0) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2012-01-01T18:00:06Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13idtjadwu5y3q2322pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.1.0.exe">Miranda IM Watcher Pack v3.1.0.exe</a> (Miranda IM Watcher Pack v3.1.0) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + + 2011-12-30T11:06:47Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12vzfygpwqng3m3b22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda_Empoli_Pack_Mod.exe">Miranda_Empoli_Pack_Mod.exe</a> (Miranda IM Empoli Pack Mod v.2.2 (core 0.9.39)) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-12-29T14:08:24Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z135in4x1yi1fd2ou04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=mirotr.7z">mirotr.7z</a> (MirOTR 0.11.0.3) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-12-25T10:59:24Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13dgrs5elfkfp0n122pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=LangpackSuite.exe">LangpackSuite.exe</a> (Test upload) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-12-25T10:58:18Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13cu14hyzzawjmev22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=LangpackSuite.exe">LangpackSuite.exe</a> (Test) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-12-21T21:55:28Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12sh1jqflzkj3ofd22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.9.exe">Miranda IM Watcher Pack v3.0.9.exe</a> (Miranda IM Watcher Pack v3.0.9) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + + 2011-12-13T19:46:02Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13nxvbpft3ezr5ps04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.8.exe">Miranda IM Watcher Pack v3.0.8.exe</a> (Miranda IM Watcher Pack v3.0.8) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + + 2011-12-04T10:52:49Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13vubiqswqqe1cf222pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Fingerprint.dll">Fingerprint.dll</a> (Fingerprint.dll WP update) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + + + + + + 2011-12-04T10:52:32Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z125fjy5qruixxloh04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=PackUpdater.dll">PackUpdater.dll</a> (PackUpdater.dll WP update) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + + + + + + 2011-12-03T15:01:42Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13etferquvuyzd0z22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=packupdater.7z">packupdater.7z</a> (Pack Updater 0.0.1.0) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-12-02T22:21:54Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12ysnhy4zi2wvudo22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=fingerprintmodplus.7z">fingerprintmodplus.7z</a> (Fingerprint Mod Plus 0.2.2.4 (latest svn revision)) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-11-28T06:15:02Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13hy1xpcr2hgbrfv22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.7.exe">Miranda IM Watcher Pack v3.0.7.exe</a> (Miranda IM Watcher Pack v3.0.7) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + + 2011-11-24T16:35:48Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13mcvxqurmeybypn04cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.6.exe">Miranda IM Watcher Pack v3.0.6.exe</a> (Miranda IM Watcher Pack v3.0.6) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + + 2011-11-23T04:58:16Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13rxxbauu3wxtfmz22pfv4p3ma4ch5kj + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=simplestatusmsg.7z">simplestatusmsg.7z</a> (Simple Status Message 1.9.0.4) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + </span> + </div> + + + + + 2011-11-19T06:13:19Z + http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13di1rprvjfhpta004cepjgmoicsl1oq1w + + <a class="ot-download-link" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.5.exe">Miranda IM Watcher Pack v3.0.5.exe</a> (Miranda IM Watcher Pack v3.0.5) file uploaded by <a class="ot-profile-link-2" href="http://code.google.com/u/watcherhd/">watcherhd</a> + + watcherhd + + + <div class="ot-labels-field-wrapper"> + <span class="ot-labels-field-name">Labels: </span> + <span class="ot-labels-field-value"> + + + <span class="ot-field-label">Featured</span> + + + + <span class="ot-field-label">Type-Installer</span> + + + + <span class="ot-field-label">OpSys-Windows</span> + + + </span> + </div> + + + + diff --git a/protocols/NewsAggregator/docs/RssText.txt b/protocols/NewsAggregator/docs/RssText.txt new file mode 100644 index 0000000000..b3334ea219 --- /dev/null +++ b/protocols/NewsAggregator/docs/RssText.txt @@ -0,0 +1,713 @@ + + + + Bash.Org.Ru + http://bash.org.ru/ + + Цитатник Рунета + ru + + b3cf2d64d57bdfea405b43426ec354e4b89a98912c71f243c93568020e2f0a70 + http://bash.org.ru/quote/414990 + Цитата #414990 + Fri, 30 Dec 2011 12:12:01 +0400 + ]]> + + + 9f3028db6c6020de238a5d27b14d85c21e5f4c8a5f96506b757925df8500c940 + http://bash.org.ru/quote/414989 + Цитата #414989 + Fri, 30 Dec 2011 11:45:18 +0400 + + + + c917324b76e57d2d6103af4b154f41fb335e9a4cc3f973b0b077a8c857c1353a + http://bash.org.ru/quote/414988 + Цитата #414988 + Fri, 30 Dec 2011 11:44:38 +0400 + ууу: конец тру
ххх: ты фильм не перепутал?
ууу: черт, в смысле, финал меня приятно удивил)]]>
+
+ + 96b70712a7185b7d1167f6f9aee454841a4246b0f270185ea3f29d034b9bd39f + http://bash.org.ru/quote/414987 + Цитата #414987 + Fri, 30 Dec 2011 11:44:01 +0400 + Света:
Купила его в ***. Классный телефон!!! Ещё не разобралась, как снять блокировку с экрана. Он красивый.]]>
+
+ + 793886d798688d3e6bec7d2ffb51d6397f356e5c0e00ef06d07f22b87643c93a + http://bash.org.ru/quote/414986 + Цитата #414986 + Fri, 30 Dec 2011 11:13:16 +0400 + + + + 6eac3f54c4f58d8a41c7e7f72f72ca6e0c6bf5869f0eefc99044ef4427db28d0 + http://bash.org.ru/quote/414985 + Цитата #414985 + Fri, 30 Dec 2011 11:12:02 +0400 + + + + 744d1d505dce7f1877c45baee1fdf97009a403d6dedb3ae1cd185480ec3dbbd0 + http://bash.org.ru/quote/414984 + Цитата #414984 + Fri, 30 Dec 2011 11:11:01 +0400 + Где купить кормовых мышей (лысых)?
На зоорынке нету.

yyy:
Довели страну... Народ уже мышей жрать начал.]]>
+
+ + aadc7f6194e8062c44250a9a878f010c0fe7d9d95af9a8389fbf8a8f748d6c6f + http://bash.org.ru/quote/414983 + Цитата #414983 + Fri, 30 Dec 2011 10:45:08 +0400 + xxx: с оптоволокном и светится
yyy: wifi бесплатно не раздает?]]>
+
+ + 01511c230d7cdf4ed42f09106b3075a5021797ae5a11d3063dc9332bb7baf858 + http://bash.org.ru/quote/414982 + Цитата #414982 + Fri, 30 Dec 2011 10:44:36 +0400 + Ergo: Да. И с включенной асей. Не бери с меня пример.]]> + + + cc05613e48d3f285e2a2a95a12f542586c5c0fe4499addf3c2a633cf4940e572 + http://bash.org.ru/quote/414981 + Цитата #414981 + Fri, 30 Dec 2011 10:44:01 +0400 + + + + 589fe99078c1447d672503cd8ffb9ba7524d9bd9e03cb5444e7c6fcd2be55269 + http://bash.org.ru/quote/414980 + Цитата #414980 + Fri, 30 Dec 2011 10:12:44 +0400 + + + + 58b960aeadb0e2700dd3ec5c18a0f9ca1f446d49f66edabf2ad3ea0f4a9f4933 + http://bash.org.ru/quote/414979 + Цитата #414979 + Fri, 30 Dec 2011 10:12:01 +0400 + Но еще глупее было наутро идти к гинекологу! >.<]]> + + + dda44cf9be0c8bf4691ce24629fd67dca371952e1dc45aa5dc1cce16ca095d29 + http://bash.org.ru/quote/414978 + Цитата #414978 + Fri, 30 Dec 2011 09:46:04 +0400 + + + + 33f18d83baf27fbd1e28e525c942b2c7e379b42a7d91f7436e2e199400ceda30 + http://bash.org.ru/quote/414977 + Цитата #414977 + Fri, 30 Dec 2011 09:45:01 +0400 + УУУ: Рулетики фигня. Все тамриэльские разбойники и мародёры поддерживают среди себя высокую грамотность и даже в самом распропащем, заросшем мхом и паутиной логове всегда есть две-три книжечки. Вот так приходит брутальный скайримский дорожный разбойник после тяжёлого трудового будня, садится на хлипкий табуретик, берёт сладкий рулетик и читает "краткую историю империи". брутальненько.]]> + + + 5599090163fe32efddbf295e457fd9cc4f3426b524eebaaaa4fa8277f34e9be2 + http://bash.org.ru/quote/414976 + Цитата #414976 + Fri, 30 Dec 2011 09:13:01 +0400 + yyy: торренты замаливает?]]> + + + 1bb26a5bc553b0570cbdf64be1a9ba3af74c3fd64dc60fe0bf9335ecb74600d0 + http://bash.org.ru/quote/414975 + Цитата #414975 + Fri, 30 Dec 2011 08:45:35 +0400 + yyy: Помогите Пете не закончить жизнь суицидом :D]]> + + + cf4da48b55d3fdb9e9825e39a664bd4d41dc5c7a64227796ad1afb0900bdec19 + http://bash.org.ru/quote/414974 + Цитата #414974 + Fri, 30 Dec 2011 08:45:01 +0400 + ]]> + + + 1a7a8ba066100acdf40b79f4fbc967932320641e3565b230678920432e80e1e3 + http://bash.org.ru/quote/414973 + Цитата #414973 + Fri, 30 Dec 2011 08:12:44 +0400 + + + + 339e00558b293e087749ba42e0f06916531e712b1edfb25e470e2c0d10494aea + http://bash.org.ru/quote/414972 + Цитата #414972 + Fri, 30 Dec 2011 08:11:44 +0400 +
XXX: Здравствуйте, скажите пожалуйста, каков срок годности у сигарет?
Купил пачку сигарет и чувствую, что какие то они хреновые, сначала подумал, что это связано с тем, что я сейчас пытаюсь бросить курить (раньше курил пачку в день, а щас пачку на неделю тяну) и начал отвыкать, потом глянул на срок изготовления, а там написано июль 2008г...

YYY: Боитесь навредить здоровью? )))]]>
+
+ + cfa6ff32cedafbf49ef812730e850a6abc9ec61c7682f3518d95fa00db4e1a14 + http://bash.org.ru/quote/414971 + Цитата #414971 + Fri, 30 Dec 2011 08:11:01 +0400 + + + + 128696d56057a228c43deb8cad49205905868817d0b4e834e37e3fdaccecafb4 + http://bash.org.ru/quote/414970 + Цитата #414970 + Thu, 29 Dec 2011 12:12:42 +0400 + yyy: Да, и глаза у тебя цвета твоих глаз.]]> + + + 2d15491a51b88af3e66c02b4d326f1e2802656b6361f8f07dd667fbbe9dce728 + http://bash.org.ru/quote/414969 + Цитата #414969 + Thu, 29 Dec 2011 12:11:34 +0400 + + + + ed64e480321c46a51035c44a445cfd98927e2e303d972ac8874e8e36c11a8371 + http://bash.org.ru/quote/414968 + Цитата #414968 + Thu, 29 Dec 2011 12:11:01 +0400 +
troydm: Абсолютно согласен с автором, именно поэтому я ложусь спать в 5 утра и просыпаюсь в час дня! Всем спокойной ночи!
muzhig: Это можно себе позволить, если нет жены. А вот моей очень не нравится когда я работаю по ночам. Все объяснения про то что «ночью намного лучше работается» она не хочет воспринимать всерьёз.
ozgg: Ну жену можно тоже научить программировать, и график синхронизируется ;)
Vexilurz: Конечно. Делов-то на 5 минут]]>
+
+ + aaa2f3a8dd486d209a697ecd4e3d728d99a2aa37dd482a3a859af5c473143b69 + http://bash.org.ru/quote/414967 + Цитата #414967 + Thu, 29 Dec 2011 11:46:07 +0400 + + + + dc470e391715c450eef79e82aaadfb0342ecc256504946f0a32d2f8c1160557e + http://bash.org.ru/quote/414966 + Цитата #414966 + Thu, 29 Dec 2011 11:45:06 +0400 +
Вот мой прадедушка, например, ухитрился прокутить и проиграть в карты до копейки все имение, наследство и женино приданое аккурат к 1917 году.

И с тех пор считался в нашем семействе непререкаемым авторитетом в вопросах распоряжения капиталом.]]>
+
+ + a7147227d4c9f64edae338c2cec9f4456a1214ea3f9d899c2486038abe99a8f5 + http://bash.org.ru/quote/414965 + Цитата #414965 + Thu, 29 Dec 2011 11:44:01 +0400 + + + + d35adf64ed961a7e7ff88e11a9b57760b1cd3f0a835f008b6e9782a27ee1a31a + http://bash.org.ru/quote/414964 + Цитата #414964 + Thu, 29 Dec 2011 11:13:11 +0400 + + + + 8ce06ad45b760d6b51e2432476e5c13dc9158f589c9b662147b760b0a1b967ac + http://bash.org.ru/quote/414963 + Цитата #414963 + Thu, 29 Dec 2011 11:12:01 +0400 +
Ну там все как надо сделали. Но барахла традиционно больше, чем места под него. Причем вот в чем загадка бытия: вынимаешь барахло из кладовки, половину выкидываешь, оставшееся убираешь в кладовку, а оно туда не влезает!]]>
+
+ + c64eb292881cc760ac0d2f7ac0b1010a8047b1271d39a0bc759076633640bbb4 + http://bash.org.ru/quote/414962 + Цитата #414962 + Thu, 29 Dec 2011 10:45:24 +0400 + Lightning: почему???
Lilith: у него клюв, и еще надо что-то сдирать как чулок
Lightning: че ты боишься, там все сходит классно
Lilith: вдруг я сдеру кальмара, а съем чулок
Lilith: а еще у них там чернильница вроде, и икра
Lightning: ну блин, ты потрошила кого-нибудь вообще когда-нибудь?
Lilith: я что, похожа на самца кальмара?
Lilith: только копченую мойву
Lightning: а курицу? не? ее так смешно еще можно надевать на руку и играть в театр
Lightning: шевелить крылышками, если через жопу вставить
Lightning: так вот, кальмара легче потрошить, он мягонький такой
Lightning: и жопы нет]]>
+
+ + 19ee3858da403abdebc2f2694ade27e224d83cb2d0325964d802f3313c1333de + http://bash.org.ru/quote/414961 + Цитата #414961 + Thu, 29 Dec 2011 10:44:36 +0400 + я: почему??
муж: да вот блин. решил показать себя в хорошем свете перед шефом. пожелал ему приятного аппетита. а он шел не в столовую а в туалет.]]>
+
+ + e0270fd309b6e90cb9f26d9012a1db8ed681fd071f5848b395535442033ffe3a + http://bash.org.ru/quote/414960 + Цитата #414960 + Thu, 29 Dec 2011 10:44:01 +0400 + BreakDancer: Разными важными делами
Luba: Например?
BreakDancer: Да вот вспомнил, что в детстве у меня был пистолет, стреляющий присосками
BreakDancer: и решил, что раз уж я вырос до 29 лет и стал большим
BreakDancer: то и присоски тоже должны быть большими!
BreakDancer: Сделал арбалет, который стреляет вантусом]]>
+
+ + 8187c704ade265c329ccc02384e4206607407657bc6c9b9425a9542f0cb5a6a1 + http://bash.org.ru/quote/414959 + Цитата #414959 + Thu, 29 Dec 2011 10:13:01 +0400 + + + + 1e5eadf8678f61893bcf84e0dfb41290f63266eab736941b7ca07653b70f17d4 + http://bash.org.ru/quote/414958 + Цитата #414958 + Thu, 29 Dec 2011 09:45:26 +0400 + Я: далее мост через реку, идете по мосту...
К: А река где в это время? Слева? Справа?
Я: ВНИЗУ!
К: ага, понятно...
Я: Дальше переходите на другой берег, спускаетесь на набережную...
К: А чё, прямо до конца моста идти? или раньше повернуть?]]>
+
+ + b7c708279640f1efe4ec3401970cb89f075864203ed0fbeeb612e9592655a4c3 + http://bash.org.ru/quote/414957 + Цитата #414957 + Thu, 29 Dec 2011 09:44:52 +0400 + Лиза фанат "Звездных войн". У нее есть фигурка Дарта Вейдера. Но девчонка есть девчонка. Она, как с пупсиком, с ним играет. Вот сегодня:
- Дарту Вейдеру нужна квартира! Я его поселю в коробке из-под апельсинов.
Вот так в женских руках даже злодеев галактического масштаба постигает судьба чебурашки.]]>
+
+ + e91f03e5e391060d916a7e7e827cb6eaeec132c17a1ac5d78af70d5368461ec5 + http://bash.org.ru/quote/414956 + Цитата #414956 + Thu, 29 Dec 2011 09:44:01 +0400 + + + + c805cfaf28bf072384933e50ea8e1c48f815063c3bfbfe05a0986d6b944bd14e + http://bash.org.ru/quote/414955 + Цитата #414955 + Thu, 29 Dec 2011 09:13:01 +0400 + YYY: Что у тебя там?
XXX: Только что коммерческий с исполнительным в кабинете орали друг на друга: "Да я таких как ты в Афгане к стенке ставил" vs "А я таких как ты на зоне петушил". Мне страшно.]]>
+
+ + 0636538a5462fc292f9b4fc6439cd6f7f8ab0fe79ba081c47e36d3d8338806fe + http://bash.org.ru/quote/414954 + Цитата #414954 + Thu, 29 Dec 2011 08:46:01 +0400 + xxx: Круговорот интернета. В твиттер постят записи пятилетней давности из ЖЖ.
yyy: вот не видела, жж обошел меня стороной
ххх: Так я и говорю, в ЖЖ постили шутки с форумов, в форумы - с фидо...
ххх: и только в фидо основоположники сетевого юмора перепечатывали анекдоты из газет]]>
+
+ + c8591055f839c5dbc882d36e7a4d2db9ce399460048e8e0e6d466dd43a88cae1 + http://bash.org.ru/quote/414953 + Цитата #414953 + Thu, 29 Dec 2011 08:13:04 +0400 + xxx: отлично! в лабораторных такого не было,но я ЗА! )))
yyy: В лабораториях опасно. зацепил не ту колбу - и все, чем гордился всю жизнь и с чем жил - с тихим шипением капает на пол... =(
xxx: Или высоковольтная установка по крестцовому сплетению... Струя эякулята пробивает кирпичную стену и ранит лектора в соседней аудитории!
xxx: Кажется, я только что придумал классное аниме, да?]]>
+
+ + 714be978a8678d201b16981150150166752145feaefa2f159980d76051602d87 + http://bash.org.ru/quote/414952 + Цитата #414952 + Thu, 29 Dec 2011 08:11:54 +0400 + Ага, я тоже первый раз неправильно прочитал =)]]> + + + dd7d36ba1543a7ac360b28c72f669816d3551bb09f34156635f80efbc9a4f390 + http://bash.org.ru/quote/414951 + Цитата #414951 + Thu, 29 Dec 2011 08:11:01 +0400 + - Вы уверены, что хотите очистить корзину? Файлы будут удалены безвозвратно.
А потом у меня порвался пакет и весь мусор разлетелся по офису, на что коллега печально заметила:
- Произошел сбой при очистке корзины, перезагрузитесь и попробуйте снова...]]>
+
+ + 174e14053635b5325be15ae0a37b47953115b33e0d082053064572907d2829a0 + http://bash.org.ru/quote/414950 + Цитата #414950 + Wed, 28 Dec 2011 12:46:01 +0400 + [QuizMaster7] Первая подсказка: Количество букв: 9
[EwokDoUrden] фигасе!
[EwokDoUrden] НАРКОМАНЫ]]>
+
+ + 691660b05378e2d242a7a60429e2cd94a44e8d2041b2facb2597cb21ec213fbf + http://bash.org.ru/quote/414949 + Цитата #414949 + Wed, 28 Dec 2011 12:13:18 +0400 + + + + 634c63c05a37bbb5933ccae9bd50bbc4e04d2a27dfd2895ab8f4be5cc77a5707 + http://bash.org.ru/quote/414948 + Цитата #414948 + Wed, 28 Dec 2011 12:12:02 +0400 + + + + 2309307a0dd79a2f77f63dbbbf3239d5de3cd41409ce23750c0e8ebecd20466f + http://bash.org.ru/quote/414946 + Цитата #414946 + Wed, 28 Dec 2011 11:44:57 +0400 + отдам в дар плеер
Explay T-7, слегка подержаный (вхламину)]]>
+
+ + c58a33a67db26dab4b121d14ebad5cd80d78040eea517835a90bbdc81887b4dc + http://bash.org.ru/quote/414945 + Цитата #414945 + Wed, 28 Dec 2011 11:44:01 +0400 + Я: Мы что, тут его и оставим?
Сотрудница: Ну да, а что нам терять? )
Я: Ну, например работу....]]>
+
+ + 3a8ab041c452b16f9f415d3f6d209f1d58f11ed2ab0ee5f928a5f6b8e22a78b9 + http://bash.org.ru/quote/414944 + Цитата #414944 + Wed, 28 Dec 2011 11:12:55 +0400 + + + + d72696e62bb007bb3004d7403e5915734f7ca02ef54757e08cb0a689a1b4df4d + http://bash.org.ru/quote/414943 + Цитата #414943 + Wed, 28 Dec 2011 11:12:01 +0400 + fintar: нужно перевезти их через реку?]]> + + + 9ffbfa3d0268a0dc955b9969e4c2b77cc50443a7d6bd9b60f9cf4102daeb7fb1 + http://bash.org.ru/quote/414942 + Цитата #414942 + Wed, 28 Dec 2011 10:45:41 +0400 + Как–то раз я в споре со своей женой обнаружил какую–то откровенную дурь в ее словах и сказал:
— Но это же не логично!
— Ну и что?
Блядь, НУ И ЧТО? в этот момент у меня вообще весь мир рухнул, и я понял что для нее вообще нет разницы, логично или нет то, что она говорит. ВООБЩЕ!

Когда я пересказываю это своим друзьям, все хохочут или улыбаются. А когда девушкам — они просто ждут, что я дальше скажу, со внимательным лицом, потому что искренне не замечают проблемы в этой фразе]]>
+
+ + 6017310e922bd529b5ad3be09f4f0c5e8b6b2fb76d0bd05b617a8eac0ae27193 + http://bash.org.ru/quote/414941 + Цитата #414941 + Wed, 28 Dec 2011 10:44:47 +0400 + + + + b101af3976aa2e9b7841576433b0c68af93c3ce85ac75383a6f7b20a0106a9c6 + http://bash.org.ru/quote/414940 + Цитата #414940 + Wed, 28 Dec 2011 10:44:01 +0400 + Она говорит:
- Давай тебе на НГ рубашку пошьем клевую и это будет моим подарком.
- Как хочешь.
Предлагает мерки поехать снять, я начинаю диктовать.
- Длина 174 см, ширина 80 см. Внутри ткань бархат, снаружи дерево, лучше дуб, крышка тоже дубовая резная.
Она говорит:
- Все записала, сейчас им позвоню.
И тут она прочитала то, что записала...]]>
+
+ + 1d3da2d78ecf5bac86c480e26d65184e8b69cab96bc8d6c038de7de9ca468ebd + http://bash.org.ru/quote/414939 + Цитата #414939 + Wed, 28 Dec 2011 10:13:07 +0400 + ххх: Обоих отправили на пересдачу с формулировкой "вылечите недотрах и приходите снова".]]> + + + 0365ddac152e7a1f45d4fe35cc5d673ff9e9cbce8d9d125e40b5dca139cd293e + http://bash.org.ru/quote/414938 + Цитата #414938 + Wed, 28 Dec 2011 10:12:01 +0400 + + + + 8774184a10feb87ab76c3eac63fcf6873b572cd376d06b87ec2cafa48464b57b + http://bash.org.ru/quote/414937 + Цитата #414937 + Wed, 28 Dec 2011 09:45:31 +0400 + + + + 5f9e58d114c8a86764bd26780e94bb3f00f0a4db026821f84d06b4540db7b5ed + http://bash.org.ru/quote/414936 + Цитата #414936 + Wed, 28 Dec 2011 09:45:01 +0400 + yyy: Я - горячая штучка. И я уже вся теку. Но будь со мной осторожен.
xxx: Аня?
yyy: У меня температура. Сопли ручьем. Рискуешь заразиться.]]>
+
+ + 8b1acd9ec8457ac318c78b1dc88005edf4fc9f0ace4d47c1080c1b670de19e2e + http://bash.org.ru/quote/414935 + Цитата #414935 + Wed, 28 Dec 2011 09:13:01 +0400 + Dimes: уринотерапия не работает]]> + + + 187c05af1edfcce0de5da82d30a58fb83ae474e6fa897ad684bd3ad7c50e9dd7 + http://bash.org.ru/quote/414934 + Цитата #414934 + Wed, 28 Dec 2011 08:46:02 +0400 + yyy: какого х?
xxx: Хрен его знает
yyy: ты показался им подозрительным? )
xxx: Был как всегда в джинсах чистых ботинках кожаной сумке и наглой рожей
xxx: Меня как правило они вообще не замечают
yyy: хы
yyy: ты был в кожаной сумке, это настораживает]]>
+
+ + 00822e572762f43d2fb56ef00c2f7be12f703a27a39949f99036f7734a26f566 + http://bash.org.ru/quote/414933 + Цитата #414933 + Wed, 28 Dec 2011 08:45:02 +0400 + misterIT: Домен рухнул, рейд заглючило. И винт прихвати.
misterIT: Сань!!! ты тут?
xxx@qip: Тут, собираюсь, а что случилось то? Напряжение? Коротнуло?
misterIT: Да хз
misterIT: Бороду вчера сбрил, свидание блядь видите ли!
xxx@qip: А я свитер постирал. Видимо не врали.]]>
+
+ + a6231be02247fcfffbaa07e020f43a4c7a1c7ed628ed86af16443ffe45ed7127 + http://bash.org.ru/quote/414932 + Цитата #414932 + Wed, 28 Dec 2011 08:12:38 +0400 + + + + 039ba4efda1619b3b4374b223e344b3423a8d9788a18bf73ac07bce7bac04bea + http://bash.org.ru/quote/414931 + Цитата #414931 + Wed, 28 Dec 2011 08:12:01 +0400 + Жена мне такая говорит:
- мне нужна шуба норковая,
- у тебя же есть,
- у меня из кусочков, а мне надо цельную, у нас у всей группы уже цельные, а z одна как лошара!
- у всей - это у одной Людки?
- ну нет, у двоих точно есть!
- ... а мне нужен фотоаппарат кэнон 5д марк 2
- у тебя же есть уже.
- ну так у меня 5д, а я хочу чтобы был марк 2, а то у всех уже в городе марки, а я один как лошара
- ну тебе-то просто хочется, а мне... (вот тут внимание!!!!) НЕОБХОДИМО.

Кемп:
хорошо, что ты не сказал, что ты им еще и деньги зарабатываешь, а то она бы тебе какую-нибудь альтернативу с шубой придумала]]>
+
+ + 66fab6cb65e11c7de896d7c6840095b9a90bd70ed8d49f8fb7adc0adb9b6a43c + http://bash.org.ru/quote/414930 + Цитата #414930 + Tue, 27 Dec 2011 13:46:01 +0400 + + + + 39b40cb066f0a9b64c53dbc67c1a521a1b3948a5b021efd4b1982753059aaee1 + http://bash.org.ru/quote/414929 + Цитата #414929 + Tue, 27 Dec 2011 13:13:01 +0400 + xxx: нет у них времени на понедельник
yyy: печалька
xxx: это уже не печалька
xxx: это уже злостька, яростька и гневик]]>
+
+ + 4f3b79bfba6994d73b3eb21530d98188f4830c26c4f8108a6d8120a4c0b5e8fc + http://bash.org.ru/quote/414928 + Цитата #414928 + Tue, 27 Dec 2011 12:46:01 +0400 + xxx: А вот интересно, если ты едешь в багажнике машины, что делать?
yyy: если ночью и в лес – то это просто плохая примета.
zzz: Во всём надо искать позитив! Я уверен, это будет последняя плохая примета в вашей жизни! :)]]>
+
+ + d3162ccb1c30aa3f654c0c6b368fa9e583c020dc3d04bcc07b6c5f02eeadaaeb + http://bash.org.ru/quote/414927 + Цитата #414927 + Tue, 27 Dec 2011 12:13:01 +0400 + xxx: Вот, кстати, ноутбук: когда закрыта крышка — лежит, а когда открыта — стоит…
yyy: Я кажется начинаю понимать, лежит — без толку, а стоит — во имя Великой Справедливости!
]]>
+
+ + 69e05b82fc5a5028a65e0435a584a83ffaa44e9784c024fb30eda9b7d87f38ba + http://bash.org.ru/quote/414926 + Цитата #414926 + Tue, 27 Dec 2011 11:46:03 +0400 + iNote: принтер жжот :D]]> + + + caa3a31eca89b56e3669c9910f23564e1ee2bd2c67f0be5472e206496b688609 + http://bash.org.ru/quote/414925 + Цитата #414925 + Tue, 27 Dec 2011 11:45:01 +0400 + xxx: Был такой случай.
xxx: Приятель сделал акустический выключатель на освещение.
xxx: Доставал книги с полки, стоя на табурете.
xxx: Упала книга, свет потух, он оступился, сломал руку.
xxx: Свет загорелся.]]>
+
+ + d3db54801b673716d6b41137bbf9ca64c62a04a1211bf43ea44876c411f88728 + http://bash.org.ru/quote/414924 + Цитата #414924 + Tue, 27 Dec 2011 11:13:12 +0400 + - Мне одной кажется, что драуги кудахтают? "Куд-ку-да", "Куд-ку-дааааа"
- Это еще что. Вчера скелет подходит ко мне и четко так: "пас-порт".
- Суровые скелеты древнескайримских пограничников. Из всего, что лепечут, можно разобрать только "паспорт" и "уходи" )]]>
+
+ + 6206e668575ef7364ca52bacdfc3d6b45da5ab80d4c69c0f1467269cec9ee6c4 + http://bash.org.ru/quote/414923 + Цитата #414923 + Tue, 27 Dec 2011 11:12:01 +0400 + xxx: Теперь никто со мной не разговаривает.
xxx: ВООБЩЕ НИКТО НИ С КЕМ НЕ РАЗГОВАРИВАЕТ! )))]]>
+
+ + 47c8ec43ac12036ac0528b4b4077f8d9f80a0fe8d4aa029f8a39b7b9d4327b6f + http://bash.org.ru/quote/414922 + Цитата #414922 + Tue, 27 Dec 2011 10:45:34 +0400 + + + + 7871bd6a1b62f062468316e82234015b27a0c448d21c39120579d4c66c1b3367 + http://bash.org.ru/quote/414921 + Цитата #414921 + Tue, 27 Dec 2011 10:45:01 +0400 + yyy: Вы аккуратней там. А то посуточно дежурить заставят и циферки вводить.]]> + + + c5fcb5c4cf429099d6540943fd5f376998aee7e5ac90409e8afc3e6a61ef177f + http://bash.org.ru/quote/414920 + Цитата #414920 + Tue, 27 Dec 2011 10:13:01 +0400 + + + + 72838435dc732ed7410037dd17b0e38588ac91999f5f195421621a8355ded16e + http://bash.org.ru/quote/414919 + Цитата #414919 + Tue, 27 Dec 2011 09:45:45 +0400 + розовый уже не модно в этом сезоне
стоял на остановке
проехал мимо розовый лимузин
одна фифа другой сказала, что это безвкусица
и это вообще уебище
ну и потом щемились со мной в автобус]]>
+
+ + 7b098f0d541bdc4ac0f90634a82e781d38e174ef380aa56ff6c94e69734fcde8 + http://bash.org.ru/quote/414918 + Цитата #414918 + Tue, 27 Dec 2011 09:44:43 +0400 + + + + 5af15998f29f5f063b9ce01b54dc8d3f0b73bb7d3f877edcc5eaa7ff8f71013b + http://bash.org.ru/quote/414917 + Цитата #414917 + Tue, 27 Dec 2011 09:44:01 +0400 + ууу: В смысле? Мама пришла?
ххх: Не, Коля мед в холодильнике нашел. Ходит по квартире, нежно прижимая к себе банку. Я сегодня Пятачок]]>
+
+ + b5a66cb535552b46159d467911174f1e4aac941c8c1d891a62619a8cdb00a3ea + http://bash.org.ru/quote/414916 + Цитата #414916 + Tue, 27 Dec 2011 09:13:02 +0400 + xxx: может быть, ты и сюжет помнишь?
yyy: очень смутно. Я совсем маленькая была, когда его в последний раз показывали...]]>
+
+ + 9e007ee3d9984de85c727f6885f913891b11676f10b053ba8a51bec6fba0e23c + http://bash.org.ru/quote/414915 + Цитата #414915 + Tue, 27 Dec 2011 08:46:11 +0400 + xxx: как служба?
yyy: как у служебной собаки
yyy: целый день спасаю мир, а вечером обещали покормить]]>
+
+ + 3dc9ddc2fad185181993315c5c3a404405ab704423d3c24bd9213dcb23bad2eb + http://bash.org.ru/quote/414914 + Цитата #414914 + Tue, 27 Dec 2011 08:45:01 +0400 + ууу: странная мораль, что так?
ххх: я человек честный, а потому злой]]>
+
+ + 61e793991c5dea07ea5a5afa2bbc899e49c23d8a0d8a1f48918915129a13a35c + http://bash.org.ru/quote/414913 + Цитата #414913 + Tue, 27 Dec 2011 08:12:04 +0400 +
ххх: Да фигня это всё!
yyy: Ну почему фигня, есть же звуки, которые как-то воздействуют на психику человека...
ххх: Да, на меня звук дрели так воздействует! Сразу хочется пойти и убить кого-нибудь!]]>
+
+ + ed2932ac0768ceb5ec1a155d4b039fb2cba050296935d7852baccb238ada1819 + http://bash.org.ru/quote/414912 + Цитата #414912 + Tue, 27 Dec 2011 08:11:31 +0400 + <xxx> оказывается, вчера я невольно купил продуктовый набор "Злые птицы": ноги куриные, грудка индейки, свиные эскалопы]]> + + + 1989a43d3a03de416d16788589caaa318ece611245b328df9b1567c13205cf70 + http://bash.org.ru/quote/414911 + Цитата #414911 + Tue, 27 Dec 2011 08:11:01 +0400 + yyy: ей-то что, у неё ж полный привод]]> + + + c207fd198261176146047e54ccfff9058582e311ab402e32dfe67837c3e61e64 + http://bash.org.ru/quote/414910 + Цитата #414910 + Mon, 26 Dec 2011 13:12:49 +0400 +
П: На что жалуетесь?
М: Ребенок отказывается пользоваться чехлом.
П: Почему? Что не устраивает?
М: Мы заказывали чехол с человеком-пауком. На чехле надпись SPIDER-MAN. Так вот, когда застегиваем молнию, становится не видно букву S.]]>
+
+ + 245f34c6c6da77a68ba258f717177e3ecb27865bd7cd72c5966a9bae3d68cdab + http://bash.org.ru/quote/414909 + Цитата #414909 + Mon, 26 Dec 2011 13:11:56 +0400 + yyy: Зачем?
xxx: Иду в гости, а у меня носок дырявый!]]>
+
+ + b9674cdf642593a5c193c3262031513822c50bcb950170cc07aadfb7fddc2649 + http://bash.org.ru/quote/414908 + Цитата #414908 + Mon, 26 Dec 2011 13:11:02 +0400 + Вазелин: студенты, я пришел
Вазелин: кому сколько?
Вазелин: пора бы уже заказывать]]>
+
+ + f5e384c1050a5d6df2cbe999fbae0e082deda5e89839ad0a913b490b3fada8c0 + http://bash.org.ru/quote/414907 + Цитата #414907 + Mon, 26 Dec 2011 12:46:14 +0400 + ххх: сначала их было несколько штук, потом подплыли еще, некоторые пикировали с воздуха)))
ххх: приличное количество собралось, в общем, некоторые вылезли на берег и подошли к нам почти вплотную
ххх: в какой-то момент мне стало стремно, что булка кончается]]>
+
+ + 71aba0126bc495b6efcd494e8b95c11b4e27fadb96f8fa31475db7aa85c7f1a4 + http://bash.org.ru/quote/414906 + Цитата #414906 + Mon, 26 Dec 2011 12:45:14 +0400 +
xxx: кто бы блять мог подумать, что именно так начинается рецензия к фильму Мушкетёры...]]>
+
+ + 3d3b28ac59593143fe32ecbb4c5df048f39482f38d336d6dc4ad119ffbeea78b + http://bash.org.ru/quote/414905 + Цитата #414905 + Mon, 26 Dec 2011 12:44:01 +0400 + yyy: но не хочет )]]> + + + 2931790e946054e81789e63e91d5dc9237bf492d3f26c6d7bc530b59ef2672fe + http://bash.org.ru/quote/414904 + Цитата #414904 + Mon, 26 Dec 2011 12:13:01 +0400 + yyy: Боюсь, что набитие морды и отъём смартфона - события сильно скоррелированные, так что Ваша проблема несколько надуманна. :)]]> + + + 5ee6027cf4758aa3989a3552db60d21acfc4bce66518265e9137abc3ce1fc3c7 + http://bash.org.ru/quote/414903 + Цитата #414903 + Mon, 26 Dec 2011 11:46:01 +0400 + Сегодня отвожу ребенка в школу, а там стенд новый повесили с фотографиями учителей. Озаглавлено гордо "Сердце нашей школы!". Рядом с фотографией трудовика в берете, маркером стрелочка и надпись "это печень".]]> + + + a47fdbea7602bfa4df71054ed3d391d00a1c0dd13f8adaa983bb5f93e5ac3d90 + http://bash.org.ru/quote/414902 + Цитата #414902 + Mon, 26 Dec 2011 11:13:01 +0400 + + + + bc8b51fdf44d63edc888ad063db2fa66d1ad92c48d999408fff00a8cbec8e4ae + http://bash.org.ru/quote/414901 + Цитата #414901 + Mon, 26 Dec 2011 10:46:12 +0400 + + + + e5174f709f5372c11eaf170b06efe25bf23fd3a65dbc9669e7b55b83ad598340 + http://bash.org.ru/quote/414900 + Цитата #414900 + Mon, 26 Dec 2011 10:45:01 +0400 + yyy: Водку
yyy: Думаю я ответил сразу на два вопроса]]>
+
+ + 0f5c7eaee411484a37e8071fb60030eb902f42a26969363233116d425faec446 + http://bash.org.ru/quote/414899 + Цитата #414899 + Mon, 26 Dec 2011 10:12:34 +0400 + + + + 1941999057f97da275dbaee167f5314de54a3de0f1ccc0f25c983cfe08845e71 + http://bash.org.ru/quote/414898 + Цитата #414898 + Mon, 26 Dec 2011 10:12:01 +0400 + + + + 1493df9ec128867419e1c9e0f8adf1e7c73eaecba9ba81e51395778f2c599faf + http://bash.org.ru/quote/414897 + Цитата #414897 + Mon, 26 Dec 2011 09:46:15 +0400 + ууу: да почему? у меня знакомые этим каждые праздники занимаются. вполне неплохо для подработки, весело + сразу деньги в кармане.
ххх: пару лет назад мы с подругой тоже раздобыли новогодних костюмов, написали веселый сценарий и развеслили объявления: "Волшебное новогоднее приключение от сестричек-Снегурочек в вашем доме".
ххх: ах вы мои неиспорченные!
ууу: ага, все правильно понял.]]>
+
+ + 507045f7af9d1dbc586b1ea528ecc9edb09ea5a1f35dab1141afc0dee491c7f2 + http://bash.org.ru/quote/414896 + Цитата #414896 + Mon, 26 Dec 2011 09:45:01 +0400 + + + + e4f30a21e7ffa737cc164ea71ddf1fe24a2c87b269a1b47db1dbf591ebe3e843 + http://bash.org.ru/quote/414895 + Цитата #414895 + Mon, 26 Dec 2011 09:12:45 +0400 + xxx: Если 12-летний сын начальника идет в серверную - к большой беде.]]> + + + 0ac1bcd3e77c115764edf2fef78568d7e88f0dcfa2830ebff7a4a67ca246192b + http://bash.org.ru/quote/414894 + Цитата #414894 + Mon, 26 Dec 2011 09:11:52 +0400 + Telecantrelem: А если предложить девушке бутерброд с колбасой - это романтика?
1143r: Telecantrelem, в определенных обстоятельствах это очень даже романтично. Например, Она выпила стакан самогону, а закусить нечем. А тут ты с бутером. Опа! На, ешь!]]>
+
+ + 0b0d1ae0708037d49d4edd71c8e1d7a488907c379787b7827be44ba7adab6cef + http://bash.org.ru/quote/414893 + Цитата #414893 + Mon, 26 Dec 2011 09:11:01 +0400 +
12.12 на складе №4 был разгружен ламинат "..." с большим количеством брака. Пачки были отсортированы мной и подписаны, хорошие пачки "Х", а плохие пачки "П". При возврате товара поставщику, начальник склада ошибочно посчитал, что "Х" означает "хуёвые", а "П" означает "пиздатые", что в корне меняло ситуацию. Грузчики его поддержали и на возврат был отгружен качественный товар. Виновным в данном инциденте себя не признаю, услуги транспортной компании оплачивать отказываюсь.]]>
+
+ + 4caeec3b83a23c4ef7e6822f1b9151c62dd719538f9719fd9dc49079edf12869 + http://bash.org.ru/quote/414892 + Цитата #414892 + Mon, 26 Dec 2011 08:46:01 +0400 + Олег: кинь туда кота)]]> + + + 533d63288a28594dd6858f5eb212c9fd62c95da02ef6c532902585a525f78b2e + http://bash.org.ru/quote/414891 + Цитата #414891 + Mon, 26 Dec 2011 08:13:01 +0400 + она: Жила-была некая функция эф от икс, и была она дифференцируема на всей эпсилон-окрестности некой точки икс нулевое
она: У меня численные методы )
она: Извини )]]>
+
+ + 92cf655d26cbe5d10b1af532ab4abb491a0d74ae9759cb81cd6dd4bb84a1d2bc + http://bash.org.ru/quote/414890 + Цитата #414890 + Sun, 25 Dec 2011 09:13:01 +0400 +
ххх: Мама, за час приняли только двух больных и они до сих пор там. передо мной 12 злых и опасных бабушек, которые уже успели закатить два скандала с применением двери и грубой физической силы тем, кто пытался пройти без очереди. Подчёркиваю: ПЫТАЛСЯ! Мне есть смысл сидеть дальше?]]>
+
+
+
\ No newline at end of file diff --git a/protocols/NewsAggregator/docs/ToDo.txt b/protocols/NewsAggregator/docs/ToDo.txt new file mode 100644 index 0000000000..09484b7932 --- /dev/null +++ b/protocols/NewsAggregator/docs/ToDo.txt @@ -0,0 +1,11 @@ +Парсить xml +экспорт\импорт +автоимпорт из файла +авторизация +замена всего html'ного + +xml parse +contacts export\import +autoimport from file service +authorization +replace all html to text \ No newline at end of file diff --git a/protocols/NewsAggregator/docs/subscriptions_test.xml b/protocols/NewsAggregator/docs/subscriptions_test.xml new file mode 100644 index 0000000000..bae46438dd --- /dev/null +++ b/protocols/NewsAggregator/docs/subscriptions_test.xml @@ -0,0 +1,169 @@ + + + + BasiL: подписки в Google Reader + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj b/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj new file mode 100644 index 0000000000..1183249dd0 --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj @@ -0,0 +1,28 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + Proto_NewsAggregator + {B97882DC-7462-41DB-A390-BDFCE5295265} + + + + + \ No newline at end of file diff --git a/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj.filters b/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj.filters new file mode 100644 index 0000000000..28f81e7f1b --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/Proto_NewsAggregator.vcxproj.filters @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico b/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico new file mode 100644 index 0000000000..b4fba290e7 Binary files /dev/null and b/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico differ diff --git a/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico b/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico new file mode 100644 index 0000000000..75c9ff1aa3 Binary files /dev/null and b/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico differ diff --git a/protocols/NewsAggregator/proto_newsaggregator/res/Proto_NewsAggregator.rc b/protocols/NewsAggregator/proto_newsaggregator/res/Proto_NewsAggregator.rc new file mode 100644 index 0000000000..814c9e4f26 --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/res/Proto_NewsAggregator.rc @@ -0,0 +1,70 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\src\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Russian (Russia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\src\\resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "Offline.ico" +IDI_ICON2 ICON "Online.ico" +#endif // Russian (Russia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/protocols/NewsAggregator/proto_newsaggregator/src/resource.h b/protocols/NewsAggregator/proto_newsaggregator/src/resource.h new file mode 100644 index 0000000000..38085445db --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/src/resource.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Proto_NewsAggr.rc +// +#define IDI_ICON1 105 +#define IDI_ICON2 104 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif -- cgit v1.2.3