diff options
Diffstat (limited to 'protocols/NewsAggregator')
35 files changed, 4813 insertions, 0 deletions
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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectName>NewsAggregator</ProjectName> + <ProjectGuid>{6DE11A47-2268-4B08-8DE5-15A1705FCE28}</ProjectGuid> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(ProjectDir)..\..\build\vc.common\plugin.props" /> + </ImportGroup> +</Project>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" /> +</Project>
\ No newline at end of file diff --git a/protocols/NewsAggregator/Res/AddFeed.ico b/protocols/NewsAggregator/Res/AddFeed.ico Binary files differnew file mode 100644 index 0000000000..6a7e9af31e --- /dev/null +++ b/protocols/NewsAggregator/Res/AddFeed.ico diff --git a/protocols/NewsAggregator/Res/CheckALL.ico b/protocols/NewsAggregator/Res/CheckALL.ico Binary files differnew file mode 100644 index 0000000000..9a720c2e07 --- /dev/null +++ b/protocols/NewsAggregator/Res/CheckALL.ico diff --git a/protocols/NewsAggregator/Res/Disabled.ico b/protocols/NewsAggregator/Res/Disabled.ico Binary files differnew file mode 100644 index 0000000000..1055879373 --- /dev/null +++ b/protocols/NewsAggregator/Res/Disabled.ico diff --git a/protocols/NewsAggregator/Res/Enabled.ico b/protocols/NewsAggregator/Res/Enabled.ico Binary files differnew file mode 100644 index 0000000000..b62ed83376 --- /dev/null +++ b/protocols/NewsAggregator/Res/Enabled.ico diff --git a/protocols/NewsAggregator/Res/Export.ico b/protocols/NewsAggregator/Res/Export.ico Binary files differnew file mode 100644 index 0000000000..945815b772 --- /dev/null +++ b/protocols/NewsAggregator/Res/Export.ico diff --git a/protocols/NewsAggregator/Res/Import.ico b/protocols/NewsAggregator/Res/Import.ico Binary files differnew file mode 100644 index 0000000000..bbe29ca82f --- /dev/null +++ b/protocols/NewsAggregator/Res/Import.ico diff --git a/protocols/NewsAggregator/Res/Main.ico b/protocols/NewsAggregator/Res/Main.ico Binary files differnew file mode 100644 index 0000000000..75c9ff1aa3 --- /dev/null +++ b/protocols/NewsAggregator/Res/Main.ico 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: #<author>#",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"#<title>#", TranslateT("empty")); + else + message.Replace(L"#<title>#", title); + + if (link.IsEmpty()) + message.Replace(L"#<link>#", TranslateT("empty")); + else + message.Replace(L"#<link>#", link); + + if (descr.IsEmpty()) + message.Replace(L"#<description>#", TranslateT("empty")); + else + message.Replace(L"#<description>#", descr); + + if (author.IsEmpty()) + message.Replace(L"#<author>#", TranslateT("empty")); + else + message.Replace(L"#<author>#", author); + + if (comments.IsEmpty()) + message.Replace(L"#<comments>#", TranslateT("empty")); + else + message.Replace(L"#<comments>#", comments); + + if (guid.IsEmpty()) + message.Replace(L"#<guid>#", TranslateT("empty")); + else + message.Replace(L"#<guid>#", guid); + + if (category.IsEmpty()) + message.Replace(L"#<category>#", TranslateT("empty")); + else + message.Replace(L"#<category>#", category); + + DBEVENTINFO olddbei = {}; + bool MesExist = false; + T2Utf pszTemp(message); + DWORD cbMemoLen = 10000, cbOrigLen = (DWORD)mir_strlen(pszTemp); + BYTE *pbBuffer = (BYTE*)mir_alloc(cbMemoLen); + for (MEVENT hDbEvent = db_event_last(hContact); hDbEvent; hDbEvent = db_event_prev(hContact, hDbEvent)) { + olddbei.cbBlob = db_event_getBlobSize(hDbEvent); + if (olddbei.cbBlob > cbMemoLen) + pbBuffer = (PBYTE)mir_realloc(pbBuffer, (size_t)(cbMemoLen = olddbei.cbBlob)); + olddbei.pBlob = pbBuffer; + db_event_get(hDbEvent, &olddbei); + + // there's no need to look for the elder events + if (stamp > 0 && olddbei.timestamp < (DWORD)stamp) + break; + + if ((DWORD)mir_strlen((char*)olddbei.pBlob) == cbOrigLen && !mir_strcmp((char*)olddbei.pBlob, pszTemp)) { + MesExist = true; + break; + } + } + mir_free(pbBuffer); + + if (!MesExist) { + if (stamp == 0) + stamp = time(0); + + T2Utf pszMessage(message); + + PROTORECVEVENT recv = { 0 }; + recv.timestamp = (DWORD)stamp; + recv.szMessage = pszMessage; + ProtoChainRecvMsg(hContact, &recv); + } +} + +void CheckCurrentFeed(MCONTACT hContact) +{ + // Check is disabled by the user? + if (!g_plugin.getByte(hContact, "CheckState", 1) != 0) + return; + + wchar_t *szURL = g_plugin.getWStringA(hContact, "URL"); + if (szURL == nullptr) + return; + + Netlib_LogfW(hNetlibUser, L"Started checking feed %s.", szURL); + + char *szData = nullptr; + GetNewsData(szURL, &szData, hContact, nullptr); + mir_free(szURL); + + if (szData == nullptr) + return; + + TiXmlDocument doc; + int ret = doc.Parse(szData); + mir_free(szData); + if (ret != ERROR_SUCCESS) + return; + + CMStringA codepage = DetectEncoding(doc); + + CMStringW szValue; + + for (auto *it : TiXmlEnum(&doc)) { + auto *szNodeName = it->Name(); + bool isRSS = !mir_strcmpi(szNodeName, "rss"), isAtom = !mir_strcmpi(szNodeName, "rdf"); + if (isRSS || isAtom) { + if (isRSS) { + if (auto *pVersion = it->Attribute("version")) { + char ver[MAX_PATH]; + mir_snprintf(ver, "RSS %s", pVersion); + g_plugin.setString(hContact, "MirVer", ver); + } + } + else if (isAtom) + g_plugin.setWString(hContact, "MirVer", L"RSS 1.0"); + + for (auto *child : TiXmlEnum(it->FirstChildElement())) { + auto *childName = child->Name(); + if (!mir_strcmpi(childName, "title")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "FirstName", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "link")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Homepage", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "description")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) { + ClearText(szValue, szChildText); + g_plugin.setWString(hContact, "About", szValue); + db_set_ws(hContact, "CList", "StatusMsg", szValue); + } + } + else if (!mir_strcmpi(childName, "language")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Language1", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "managingEditor")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "e-mail", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "category")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Interest0Text", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(childName, "copyright")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + db_set_s(hContact, "UserInfo", "MyNotes", _T2A(ClearText(szValue, szChildText))); + } + else if (!mir_strcmpi(childName, "image")) { + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + else if (!mir_strcmpi(childName, "lastBuildDate")) { + time_t stamp = DateToUnixTime(child->GetText(), 0); + double deltaupd = difftime(time(0), stamp); + double deltacheck = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck")); + if (deltaupd - deltacheck >= 0) { + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + return; + } + } + else if (!mir_strcmpi(childName, "item")) { + CMStringW title, link, descr, author, comments, guid, category; + time_t stamp = 0; + for (auto *itemval : TiXmlEnum(child)) { + auto *itemName = itemval->Name(); + ptrW value(EncodeResult(itemval->GetText(), codepage)); + + // We only use the first tag for now and ignore the rest. + if (!mir_strcmpi(itemName, "title")) + ClearText(title, value); + + else if (!mir_strcmpi(itemName, "link")) + ClearText(link, value); + + else if (!mir_strcmpi(itemName, "pubDate") || !mir_strcmpi(itemName, "date")) { + if (stamp == 0) + stamp = DateToUnixTime(itemval->GetText(), 0); + } + else if (!mir_strcmpi(itemName, "description") || !mir_strcmpi(itemName, "encoded")) + ClearText(descr, value); + + else if (!mir_strcmpi(itemName, "author") || !mir_strcmpi(itemName, "creator")) + ClearText(author, value); + + else if (!mir_strcmpi(itemName, "comments")) + ClearText(comments, value); + + else if (!mir_strcmpi(itemName, "guid")) + ClearText(guid, value); + + else if (!mir_strcmpi(itemName, "category")) + ClearText(category, value); + } + + XmlToMsg(hContact, title, link, descr, author, comments, guid, category, stamp); + } + } + } + else if (!mir_strcmpi(szNodeName, "feed")) { + g_plugin.setWString(hContact, "MirVer", L"Atom 3"); + for (auto *child : TiXmlEnum(it)) { + auto *szChildName = child->Name(); + if (!mir_strcmpi(szChildName, "title")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "FirstName", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "link")) { + if (!child->Attribute("rel", "self")) + if (auto *pHref = child->Attribute("href")) + g_plugin.setWString(hContact, "Homepage", Utf2T(pHref)); + } + else if (!mir_strcmpi(szChildName, "subtitle")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) { + ClearText(szValue, szChildText); + g_plugin.setWString(hContact, "About", szValue); + db_set_ws(hContact, "CList", "StatusMsg", szValue); + } + } + else if (!mir_strcmpi(szChildName, "language")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Language1", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "author")) { + for (auto *authorval : TiXmlFilter(child, "email")) { + g_plugin.setWString(hContact, "e-mail", ptrW(EncodeResult(authorval->GetText(), codepage))); + break; + } + } + else if (!mir_strcmpi(szChildName, "category")) { + ptrW szChildText(EncodeResult(child->GetText(), codepage)); + if (szChildText) + g_plugin.setWString(hContact, "Interest0Text", ClearText(szValue, szChildText)); + } + else if (!mir_strcmpi(szChildName, "icon")) { + for (auto *imageval : TiXmlFilter(child, "url")) + SetAvatar(hContact, imageval->GetText()); + } + else if (!mir_strcmpi(szChildName, "updated")) { + time_t stamp = DateToUnixTime(child->GetText(), 1); + double deltaupd = difftime(time(0), stamp); + double deltacheck = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck")); + if (deltaupd - deltacheck >= 0) { + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + return; + } + } + else if (!mir_strcmpi(szChildName, "entry")) { + CMStringW title, link, descr, author, comments, guid, category; + time_t stamp = 0; + for (auto *itemval : TiXmlEnum(child)) { + LPCSTR szItemName = itemval->Name(); + if (!mir_strcmpi(szItemName, "title")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(title, szItemText); + } + else if (!mir_strcmpi(szItemName, "link")) { + if (auto *pszLink = itemval->Attribute("href")) + ClearText(link, ptrW(EncodeResult(pszLink, codepage))); + } + else if (!mir_strcmpi(szItemName, "updated")) { + if (stamp == 0) + stamp = DateToUnixTime(itemval->GetText(), 0); + } + else if (!mir_strcmpi(szItemName, "summary") || !mir_strcmpi(szItemName, "content")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(descr, szItemText); + } + else if (!mir_strcmpi(szItemName, "author")) { + for (auto *authorval : TiXmlFilter(itemval, "name")) { + ptrW szItemText(EncodeResult(authorval->GetText(), codepage)); + if (szItemText) + ClearText(author, szItemText); + break; + } + } + else if (!mir_strcmpi(szItemName, "comments")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(comments, szItemText); + } + else if (!mir_strcmpi(szItemName, "id")) { + ptrW szItemText(EncodeResult(itemval->GetText(), codepage)); + if (szItemText) + ClearText(guid, szItemText); + } + else if (!mir_strcmpi(szItemName, "category")) { + if (auto *p = itemval->Attribute("term")) + ClearText(link, ptrW(EncodeResult(p, codepage))); + } + } + + XmlToMsg(hContact, title, link, descr, author, comments, guid, category, stamp); + } + } + } + } + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// downloads avatars from a given feed + +void CheckCurrentFeedAvatar(MCONTACT hContact) +{ + if (!g_plugin.getByte(hContact, "CheckState", 1)) + return; + + wchar_t *szURL = g_plugin.getWStringA(hContact, "URL"); + if (szURL == nullptr) + return; + + char *szData = nullptr; + GetNewsData(szURL, &szData, hContact, nullptr); + mir_free(szURL); + + if (szData == nullptr) + return; + + TiXmlDocument doc; + int ret = doc.Parse(szData); + mir_free(szData); + if (ret != ERROR_SUCCESS) + return; + + for (auto *it : TiXmlEnum(&doc)) { + auto *szNodeName = it->Name(); + if (!mir_strcmpi(szNodeName, "rss") || !mir_strcmpi(szNodeName, "rdf")) { + for (auto *child : TiXmlFilter(it->FirstChildElement(), "image")) + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + else if (!mir_strcmpi(szNodeName, "feed")) { + for (auto *child : TiXmlFilter(it, "icon")) + for (auto *xmlImage : TiXmlFilter(child, "url")) + SetAvatar(hContact, xmlImage->GetText()); + } + } +} diff --git a/protocols/NewsAggregator/Src/Icons.cpp b/protocols/NewsAggregator/Src/Icons.cpp new file mode 100644 index 0000000000..b9cdeb6166 --- /dev/null +++ b/protocols/NewsAggregator/Src/Icons.cpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +static IconItem iconList[] = +{ + { LPGEN("Protocol icon"), "main", IDI_ICON}, + { LPGEN("Check All Feeds"), "checkall", IDI_CHECKALL}, + { LPGEN("Add Feed"), "addfeed", IDI_ADDFEED}, + { LPGEN("Import Feeds"), "importfeeds", IDI_IMPORTFEEDS}, + { LPGEN("Export Feeds"), "exportfeeds", IDI_EXPORTFEEDS}, + { LPGEN("Check Feed"), "checkfeed", IDI_CHECKALL}, + { LPGEN("Auto Update Enabled"), "enabled", IDI_ENABLED}, + { LPGEN("Auto Update Disabled"), "disabled", IDI_DISABLED} +}; + +void InitIcons() +{ + g_plugin.registerIcon(LPGEN("News Aggregator"), iconList, MODULENAME); +} + +HICON LoadIconEx(const char *name, bool big) +{ + char szSettingName[100]; + mir_snprintf(szSettingName, "%s_%s", MODULENAME, name); + return IcoLib_GetIcon(szSettingName, big); +} + +HANDLE GetIconHandle(const char *name) +{ + for (int i=0; i < _countof(iconList); i++) + if ( !mir_strcmp(iconList[i].szName, name)) + return iconList[i].hIcolib; + + return nullptr; +} diff --git a/protocols/NewsAggregator/Src/Menus.cpp b/protocols/NewsAggregator/Src/Menus.cpp new file mode 100644 index 0000000000..7770e843eb --- /dev/null +++ b/protocols/NewsAggregator/Src/Menus.cpp @@ -0,0 +1,83 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HGENMENU hService2[7]; + +void InitMenu() +{ + CMenuItem mi(&g_plugin); + mi.flags = CMIF_UNICODE | CMIF_NOTOFFLINE; + mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("News Aggregator"), 500099000); + Menu_ConfigureItem(mi.root, MCI_OPT_UID, "D9733E4F-1946-4390-8EB3-591E8687222E"); + + SET_UID(mi, 0x3ec91864, 0xefa7, 0x4994, 0xb7, 0x75, 0x6c, 0x96, 0xcb, 0x29, 0x2f, 0x93); + mi.position = 10100001; + if (g_plugin.getByte("AutoUpdate", 1)) + mi.name.w = LPGENW("Auto Update Enabled"); + else + mi.name.w = LPGENW("Auto Update Disabled"); + mi.hIcolibItem = GetIconHandle("main"); + mi.pszService = MS_NEWSAGGREGATOR_ENABLED; + hService2[0] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0x8076bb4d, 0x1e44, 0x43af, 0x97, 0x1e, 0x31, 0xd8, 0xa4, 0xe9, 0xb8, 0x37); + mi.position = 20100001; + mi.name.w = LPGENW("Check All Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_CHECKALLFEEDS; + hService2[1] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0xb876484d, 0x28aa, 0x4e03, 0x9e, 0x98, 0xed, 0xbc, 0xd1, 0xcf, 0x31, 0x80); + mi.position = 20100002; + mi.hIcolibItem = GetIconHandle("addfeed"); + mi.name.w = LPGENW("Add Feed"); + mi.pszService = MS_NEWSAGGREGATOR_ADDFEED; + hService2[2] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0x600bf2c2, 0xa974, 0x44d3, 0x98, 0xf9, 0xe6, 0x65, 0x7c, 0x1f, 0x63, 0x37); + mi.position = 20100003; + mi.hIcolibItem = GetIconHandle("importfeeds"); + mi.name.w = LPGENW("Import Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_IMPORTFEEDS; + hService2[3] = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0xc09c8119, 0x64c2, 0x49bd, 0x81, 0xf, 0x54, 0x20, 0x69, 0xd7, 0x30, 0xcf); + mi.position = 20100004; + mi.hIcolibItem = GetIconHandle("exportfeeds"); + mi.name.w = LPGENW("Export Feeds"); + mi.pszService = MS_NEWSAGGREGATOR_EXPORTFEEDS; + hService2[4] = Menu_AddMainMenuItem(&mi); + + // adding contact menu items + SET_UID(mi, 0x92be499c, 0x928c, 0x4789, 0x8f, 0x36, 0x28, 0xa2, 0x9f, 0xb7, 0x1a, 0x97); + mi.root = nullptr; + mi.position = -0x7FFFFFFA; + mi.hIcolibItem = GetIconHandle("checkfeed"); + mi.name.w = LPGENW("Check feed"); + mi.pszService = MS_NEWSAGGREGATOR_CHECKFEED; + hService2[5] = Menu_AddContactMenuItem(&mi, MODULENAME); + + SET_UID(mi, 0x41a70fbc, 0x9241, 0x44c0, 0x90, 0x90, 0x87, 0xd2, 0xc5, 0x9f, 0xc9, 0xac); + mi.name.w = LPGENW("Change feed"); + mi.pszService = MS_NEWSAGGREGATOR_CHANGEFEED; + hService2[6] = Menu_AddContactMenuItem(&mi, MODULENAME); + + Menu_ModifyItem(hService2[0], nullptr, GetIconHandle(g_plugin.getByte("AutoUpdate", 1) ? "enabled" : "disabled")); +} diff --git a/protocols/NewsAggregator/Src/NewsAggregator.cpp b/protocols/NewsAggregator/Src/NewsAggregator.cpp new file mode 100644 index 0000000000..8ee17dd97a --- /dev/null +++ b/protocols/NewsAggregator/Src/NewsAggregator.cpp @@ -0,0 +1,106 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HANDLE hPrebuildMenuHook = nullptr; +CDlgBase *pAddFeedDialog = nullptr, *pImportDialog = nullptr, *pExportDialog = nullptr; +wchar_t tszRoot[MAX_PATH] = {0}; +HANDLE hUpdateMutex; + +LIST<CFeedEditor> g_arFeeds(1, PtrKeySortT); + +CMPlugin g_plugin; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {56CC3F29-CCBF-4546-A8BA-9856248A412A} + {0x56cc3f29, 0xccbf, 0x4546, {0xa8, 0xba, 0x98, 0x56, 0x24, 0x8a, 0x41, 0x2a}} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) +{ + RegisterProtocol(PROTOTYPE_VIRTUAL); + SetUniqueId("URL"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Load() +{ + // Add options hook + HookEvent(ME_OPT_INITIALISE, OptInit); + HookEvent(ME_SYSTEM_MODULESLOADED, NewsAggrInit); + HookEvent(ME_SYSTEM_PRESHUTDOWN, NewsAggrPreShutdown); + + hUpdateMutex = CreateMutex(nullptr, FALSE, nullptr); + + CreateProtoServiceFunction(MODULENAME, PS_GETNAME, NewsAggrGetName); + CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, NewsAggrGetCaps); + CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, NewsAggrSetStatus); + CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, NewsAggrGetStatus); + CreateProtoServiceFunction(MODULENAME, PS_LOADICON, NewsAggrLoadIcon); + CreateProtoServiceFunction(MODULENAME, PSS_GETINFO, NewsAggrGetInfo); + CreateProtoServiceFunction(MODULENAME, PS_GETAVATARINFO, NewsAggrGetAvatarInfo); + CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, NewsAggrRecvMessage); + + CreateServiceFunction(MS_NEWSAGGREGATOR_CHECKALLFEEDS, CheckAllFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_ADDFEED, AddFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_IMPORTFEEDS, ImportFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_EXPORTFEEDS, ExportFeeds); + CreateServiceFunction(MS_NEWSAGGREGATOR_CHECKFEED, CheckFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_CHANGEFEED, ChangeFeed); + CreateServiceFunction(MS_NEWSAGGREGATOR_ENABLED, EnableDisable); + + HOTKEYDESC hkd = {}; + hkd.dwFlags = HKD_UNICODE; + hkd.pszName = "NewsAggregator/CheckAllFeeds"; + hkd.szDescription.w = LPGENW("Check All Feeds"); + hkd.szSection.w = LPGENW("News Aggregator"); + hkd.pszService = MS_NEWSAGGREGATOR_CHECKALLFEEDS; + hkd.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL+HKCOMB_A, 'O') | HKF_MIRANDA_LOCAL; + g_plugin.addHotkey(&hkd); + + InitIcons(); + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Unload() +{ + DestroyUpdateList(); + CloseHandle(hUpdateMutex); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Options.cpp b/protocols/NewsAggregator/Src/Options.cpp new file mode 100644 index 0000000000..6239ce15c1 --- /dev/null +++ b/protocols/NewsAggregator/Src/Options.cpp @@ -0,0 +1,1016 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +CExportFeed::CExportFeed() + : CSuper(g_plugin, IDD_FEEDEXPORT), + m_feedslist(this, IDC_FEEDSLIST), m_feedsexportlist(this, IDC_FEEDSEXPORTLIST), + m_addfeed(this, IDC_ADDFEED), m_removefeed(this, IDC_REMOVEFEED), + m_addallfeeds(this, IDC_ADDALLFEEDS), m_removeallfeeds(this, IDC_REMOVEALLFEEDS), + m_ok(this, IDOK) +{ + m_addfeed.OnClick = Callback(this, &CExportFeed::OnAddFeed); + m_removefeed.OnClick = Callback(this, &CExportFeed::OnRemoveFeed); + m_addallfeeds.OnClick = Callback(this, &CExportFeed::OnAddAllFeeds); + m_removeallfeeds.OnClick = Callback(this, &CExportFeed::OnRemoveAllFeeds); + m_ok.OnClick = Callback(this, &CExportFeed::OnOk); + + m_feedslist.OnDblClick = Callback(this, &CExportFeed::OnFeedsList); + m_feedsexportlist.OnDblClick = Callback(this, &CExportFeed::OnFeedsExportList); +} + +bool CExportFeed::OnInitDialog() +{ + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "ExportDlg"); + for (auto &hContact : Contacts(MODULENAME)) { + ptrW message(g_plugin.getWStringA(hContact, "Nick")); + if (message != nullptr) + m_feedslist.AddString(message); + } + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } + return true; +} + +void CExportFeed::OnAddFeed(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsexportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CExportFeed::OnRemoveFeed(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsexportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsexportlist.DeleteString(cursel); + if (!m_feedsexportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CExportFeed::OnAddAllFeeds(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int count = m_feedslist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(i, item, _countof(item)); + m_feedsexportlist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedslist.DeleteString(i); + m_addfeed.Disable(); + m_addallfeeds.Disable(); +} + +void CExportFeed::OnRemoveAllFeeds(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int count = m_feedsexportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(i, item, _countof(item)); + m_feedslist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedsexportlist.DeleteString(i); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); +} + +void CExportFeed::OnFeedsList(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsexportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CExportFeed::OnFeedsExportList(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsexportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsexportlist.DeleteString(cursel); + if (!m_feedsexportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +static const TiXmlElement* AdviceNode(const TiXmlElement *node) +{ + auto *tmpnode = node; + + // try to rest on the same level first + node = node->NextSiblingElement(); + if (node) + return node; + + do { + // go up one level + node = tmpnode->Parent()->ToElement(); + tmpnode = node; + node = node->NextSiblingElement(); + if (node) + return node; + } + while (mir_strcmpi(tmpnode->Name(), "body")); + + // nothing found or we reached body + return nullptr; +} + +void CExportFeed::OnOk(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + VARSW tszMirDir(L"%miranda_path%"); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = sizeof(ofn); + wchar_t tmp[MAX_PATH]; + mir_snwprintf(tmp, L"%s (*.opml)%c*.opml%c%c", TranslateT("OPML files"), 0, 0, 0); + ofn.lpstrFilter = tmp; + ofn.hwndOwner = nullptr; + ofn.lpstrFile = FileName; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_SHAREAWARE | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; + ofn.lpstrInitialDir = tszMirDir; + *FileName = '\0'; + ofn.lpstrDefExt = L""; + if (!GetSaveFileName(&ofn)) + return; + + TiXmlDocument doc; + auto *hXml = doc.NewElement("opml"); doc.InsertEndChild(hXml); + hXml->SetAttribute("version", "1.0"); + + auto *xmlHeader = doc.NewElement("head"); hXml->InsertEndChild(xmlHeader); + auto *xmlTitle = doc.NewElement("title"); xmlTitle->SetText("Miranda NG NewsAggregator plugin export"); xmlHeader->InsertEndChild(xmlTitle); + + auto *xmlBody = doc.NewElement("body"); hXml->InsertEndChild(xmlBody); + + int count = m_feedsexportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsexportlist.GetItemText(i, item, _countof(item)); + MCONTACT hContact = GetContactByNick(item); + wchar_t + *title = g_plugin.getWStringA(hContact, "Nick"), + *url = g_plugin.getWStringA(hContact, "URL"), + *siteurl = g_plugin.getWStringA(hContact, "Homepage"), + *group = db_get_wsa(hContact, "CList", "Group"); + + TiXmlElement *elem = xmlBody; + if (group) { + wchar_t *section = wcstok(group, L"\\"); + while (section != nullptr) { + TiXmlElement *existgroup = 0; + for (auto *it : TiXmlFilter(elem, "outline")) { + if (it->Attribute("title", T2Utf(section))) { + existgroup = (TiXmlElement*)it; + break; + } + } + + if (!existgroup) { + auto *pNew = doc.NewElement("outline"); + pNew->SetAttribute("title", section); pNew->SetAttribute("text", section); + elem->InsertEndChild(pNew); + elem = pNew; + } + else elem = existgroup; + + section = wcstok(nullptr, L"\\"); + } + } + + auto *pNew = doc.NewElement("outline"); elem->InsertEndChild(pNew); + pNew->SetAttribute("text", title); + pNew->SetAttribute("title", title); + pNew->SetAttribute("type", "rss"); + pNew->SetAttribute("xmlUrl", url); + pNew->SetAttribute("htmlUrl", siteurl); + + mir_free(title); + mir_free(url); + mir_free(siteurl); + mir_free(group); + } + + FILE *out = _wfopen(FileName, L"wb"); + if (out) { + tinyxml2::XMLPrinter printer(out); + doc.Print(&printer); + fclose(out); + } +} + +bool CExportFeed::OnClose() +{ + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, "ExportDlg"); + if (pExportDialog) + pExportDialog = nullptr; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CImportFeed::CImportFeed(CCtrlListView *m_feeds) + : CSuper(g_plugin, IDD_FEEDIMPORT), + m_importfile(this, IDC_IMPORTFILEPATH), m_browsefile(this, IDC_BROWSEIMPORTFILE), + m_feedslist(this, IDC_FEEDSLIST), m_feedsimportlist(this, IDC_FEEDSIMPORTLIST), + m_addfeed(this, IDC_ADDFEED), m_removefeed(this, IDC_REMOVEFEED), + m_addallfeeds(this, IDC_ADDALLFEEDS), m_removeallfeeds(this, IDC_REMOVEALLFEEDS), + m_ok(this, IDOK) +{ + m_list = m_feeds; + m_browsefile.OnClick = Callback(this, &CImportFeed::OnBrowseFile); + m_addfeed.OnClick = Callback(this, &CImportFeed::OnAddFeed); + m_removefeed.OnClick = Callback(this, &CImportFeed::OnRemoveFeed); + m_addallfeeds.OnClick = Callback(this, &CImportFeed::OnAddAllFeeds); + m_removeallfeeds.OnClick = Callback(this, &CImportFeed::OnRemoveAllFeeds); + m_ok.OnClick = Callback(this, &CImportFeed::OnOk); + + m_feedslist.OnDblClick = Callback(this, &CImportFeed::OnFeedsList); + m_feedsimportlist.OnDblClick = Callback(this, &CImportFeed::OnFeedsImportList); +} + +bool CImportFeed::OnInitDialog() +{ + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "ImportDlg"); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + m_addfeed.Disable(); + m_addallfeeds.Disable(); + return true; +} + +void CImportFeed::OnBrowseFile(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + VARSW tszMirDir(L"%miranda_path%"); + + OPENFILENAME ofn = { 0 }; + ofn.lStructSize = sizeof(ofn); + wchar_t tmp[MAX_PATH]; + mir_snwprintf(tmp, L"%s (*.opml, *.xml)%c*.opml;*.xml%c%c", TranslateT("OPML files"), 0, 0, 0); + ofn.lpstrFilter = tmp; + ofn.hwndOwner = nullptr; + ofn.lpstrFile = FileName; + ofn.nMaxFile = MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY; + ofn.lpstrInitialDir = tszMirDir; + *FileName = '\0'; + ofn.lpstrDefExt = L""; + if (!GetOpenFileName(&ofn)) + return; + + FILE *in = _wfopen(FileName, L"rb"); + if (in == nullptr) + return; + + TiXmlDocument doc; + int res = doc.LoadFile(in); + fclose(in); + if (res != 0) { + MessageBox(m_hwnd, TranslateT("Not valid import file."), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + + m_importfile.SetText(FileName); + + auto *node = TiXmlConst(&doc)["opml"]["body"]["outline"].ToElement(); + if (!node) + node = TiXmlConst(&doc)["body"]["outline"].ToElement(); + if (node == nullptr) { + MessageBox(m_hwnd, TranslateT("Not valid import file."), TranslateT("Error"), MB_OK | MB_ICONERROR); + return; + } + + while (node) { + auto *pszUrl = node->Attribute("xmlUrl"); + if (!pszUrl && node->NoChildren()) + node = AdviceNode(node); + else if (!pszUrl && !node->NoChildren()) + node = node->FirstChildElement(); + else if (pszUrl) { + if (auto *pszText = node->Attribute("text")) { + Utf2T text(pszText); + m_feedslist.AddString(text); + m_addfeed.Enable(); + m_addallfeeds.Enable(); + } + + node = AdviceNode(node); + } + } +} + +void CImportFeed::OnAddFeed(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsimportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CImportFeed::OnRemoveFeed(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsimportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsimportlist.DeleteString(cursel); + if (!m_feedsimportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CImportFeed::OnAddAllFeeds(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int count = m_feedslist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(i, item, _countof(item)); + m_feedsimportlist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedslist.DeleteString(i); + m_addfeed.Disable(); + m_addallfeeds.Disable(); +} + +void CImportFeed::OnRemoveAllFeeds(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int count = m_feedsimportlist.GetCount(); + for (int i = 0; i < count; i++) { + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(i, item, _countof(item)); + m_feedslist.AddString(item); + } + for (int i = count - 1; i > -1; i--) + m_feedsimportlist.DeleteString(i); + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); +} + +void CImportFeed::OnFeedsList(CCtrlBase*) +{ + if (!m_removefeed.Enabled()) + m_removefeed.Enable(); + if (!m_removeallfeeds.Enabled()) + m_removeallfeeds.Enable(); + if (!m_ok.Enabled()) + m_ok.Enable(); + int cursel = m_feedslist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedslist.GetItemText(cursel, item, _countof(item)); + m_feedsimportlist.AddString(item); + m_feedslist.DeleteString(cursel); + if (!m_feedslist.GetCount()) { + m_addfeed.Disable(); + m_addallfeeds.Disable(); + } +} + +void CImportFeed::OnFeedsImportList(CCtrlBase*) +{ + if (!m_addfeed.Enabled()) + m_addfeed.Enable(); + if (!m_addallfeeds.Enabled()) + m_addallfeeds.Enable(); + int cursel = m_feedsimportlist.GetCurSel(); + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(cursel, item, _countof(item)); + m_feedslist.AddString(item); + m_feedsimportlist.DeleteString(cursel); + if (!m_feedsimportlist.GetCount()) { + m_removefeed.Disable(); + m_removeallfeeds.Disable(); + m_ok.Disable(); + } +} + +void CImportFeed::OnOk(CCtrlBase*) +{ + wchar_t FileName[MAX_PATH]; + m_importfile.GetText(FileName, _countof(FileName)); + + FILE *in = _wfopen(FileName, L"rb"); + if (in == nullptr) + return; + + TiXmlDocument doc; + int res = doc.LoadFile(in); + fclose(in); + if (res != 0) + return; + + auto *node = TiXmlConst(&doc)["opml"]["body"]["outline"].ToElement(); + if (!node) + node = TiXmlConst(&doc)["body"]["outline"].ToElement(); + if (node == nullptr) + return; + + int count = m_feedsimportlist.GetCount(); + int DUPES = 0; + + while (node) { + auto *pszUrl = node->Attribute("xmlUrl"); + if (!pszUrl && node->NoChildren()) + node = AdviceNode(node); + else if (!pszUrl && !node->NoChildren()) + node = node->FirstChildElement(); + else if (pszUrl) { + wchar_t *text = nullptr, *url = nullptr, *siteurl = nullptr; + bool bNeedToImport = false; + + if (auto *pszText = node->Attribute("text")) { + text = mir_utf8decodeW(pszText); + + for (int j = 0; j < count; j++) { + wchar_t item[MAX_PATH]; + m_feedsimportlist.GetItemText(j, item, _countof(item)); + if (!mir_wstrcmpi(item, text)) { + bNeedToImport = true; + break; + } + } + } + + if (auto *pszText = node->Attribute("xmlUrl")) { + url = mir_utf8decodeW(pszText); + if (GetContactByURL(url) && bNeedToImport) { + bNeedToImport = false; + DUPES++; + } + } + + if (auto *pszText = node->Attribute("htmlUrl")) + siteurl = mir_utf8decodeW(pszText); + + if (bNeedToImport && text && url && siteurl) { + CMStringW wszGroup; + auto *parent = node->Parent()->ToElement(); + while (mir_strcmpi(parent->Name(), "body")) { + if (auto *pszText = parent->Attribute("text")) { + if (!wszGroup.IsEmpty()) + wszGroup.Insert(0, L"\\"); + wszGroup.Insert(0, Utf2T(pszText)); + } + parent = parent->Parent()->ToElement(); + } + + MCONTACT hContact = db_add_contact(); + Proto_AddToContact(hContact, MODULENAME); + g_plugin.setWString(hContact, "Nick", text); + g_plugin.setWString(hContact, "URL", url); + g_plugin.setWString(hContact, "Homepage", siteurl); + g_plugin.setByte(hContact, "CheckState", 1); + g_plugin.setDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME); + g_plugin.setWString(hContact, "MsgFormat", TAGSDEFAULT); + g_plugin.setWord(hContact, "Status", Proto_GetStatus(MODULENAME)); + + if (m_list != nullptr) { + int iItem = m_list->AddItem(text, -1); + m_list->SetItem(iItem, 1, url); + m_list->SetCheckState(iItem, 1); + } + + if (!wszGroup.IsEmpty()) { + db_set_ws(hContact, "CList", "Group", wszGroup); + Clist_GroupCreate(0, wszGroup); + } + } + mir_free(text); + mir_free(url); + mir_free(siteurl); + + node = AdviceNode(node); + } + } + + wchar_t mes[MAX_PATH]; + if (DUPES) + mir_snwprintf(mes, TranslateT("Imported %d feed(s)\r\nNot imported %d duplicate(s)."), count - DUPES, DUPES); + else + mir_snwprintf(mes, TranslateT("Imported %d feed(s)."), count); + MessageBox(m_hwnd, mes, TranslateT("News Aggregator"), MB_OK | MB_ICONINFORMATION); +} + +bool CImportFeed::OnClose() +{ + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, "ImportDlg"); + if (pImportDialog) + pImportDialog = nullptr; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CFeedEditor::CFeedEditor(int iItem, CCtrlListView *m_feeds, MCONTACT Contact) + : CSuper(g_plugin, IDD_ADDFEED), + m_feedtitle(this, IDC_FEEDTITLE), m_feedurl(this, IDC_FEEDURL), + m_checktime(this, IDC_CHECKTIME), m_checktimespin(this, IDC_TIMEOUT_VALUE_SPIN, 999), + m_checkfeed(this, IDC_DISCOVERY), m_useauth(this, IDC_USEAUTH), + m_login(this, IDC_LOGIN), m_password(this, IDC_PASSWORD), + m_tagedit(this, IDC_TAGSEDIT), m_reset(this, IDC_RESET), + m_help(this, IDC_TAGHELP), m_ok(this, IDOK), m_iItem(iItem) +{ + m_list = m_feeds; + m_hContact = Contact; + m_checkfeed.OnClick = Callback(this, &CFeedEditor::OnCheckFeed); + m_useauth.OnChange = Callback(this, &CFeedEditor::OnUseAuth); + m_reset.OnClick = Callback(this, &CFeedEditor::OnReset); + m_help.OnClick = Callback(this, &CFeedEditor::OnHelp); + m_ok.OnClick = Callback(this, &CFeedEditor::OnOk); +} + +bool CFeedEditor::OnInitDialog() +{ + if (m_iItem == -1 && m_hContact == NULL) + SetWindowText(m_hwnd, TranslateT("Add Feed")); + else + SetWindowText(m_hwnd, TranslateT("Change Feed")); + m_checktime.SetMaxLength(3); + + if (m_iItem > -1 && m_hContact == 0) { + wchar_t SelNick[MAX_PATH], SelUrl[MAX_PACKAGE_NAME]; + m_list->GetItemText(m_iItem, 0, SelNick, _countof(SelNick)); + m_list->GetItemText(m_iItem, 1, SelUrl, _countof(SelNick)); + + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + if ((dbNick == NULL) || (mir_wstrcmp(dbNick, SelNick) != 0)) + continue; + + ptrW dbURL(g_plugin.getWStringA(hContact, "URL")); + if ((dbURL == NULL) || (mir_wstrcmp(dbURL, SelUrl) != 0)) + continue; + + m_hContact = hContact; + m_feedtitle.SetText(SelNick); + m_feedurl.SetText(SelUrl); + m_checktime.SetInt(g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)); + + ptrW szMsgFormat(g_plugin.getWStringA(hContact, "MsgFormat")); + if (szMsgFormat) + m_tagedit.SetText(szMsgFormat); + + if (g_plugin.getByte(hContact, "UseAuth", 0)) { + m_useauth.SetState(1); + m_login.Enable(); + m_password.Enable(); + + ptrW szLogin(g_plugin.getWStringA(hContact, "Login")); + if (szLogin) + m_login.SetText(szLogin); + + pass_ptrA pwd(g_plugin.getStringA(hContact, "Password")); + m_password.SetTextA(pwd); + } + g_arFeeds.insert(this); + Utils_RestoreWindowPositionNoSize(m_hwnd, hContact, MODULENAME, "ChangeDlg"); + break; + } + } + else if (m_iItem == -1 && m_hContact == NULL) { + m_feedurl.SetText(L"http://"); + m_tagedit.SetText(TAGSDEFAULT); + m_checktime.SetInt(DEFAULT_UPDATE_TIME); + Utils_RestoreWindowPositionNoSize(m_hwnd, NULL, MODULENAME, "AddDlg"); + } + else if (m_hContact != NULL) { + ptrW dbNick(g_plugin.getWStringA(m_hContact, "Nick")); + ptrW dbURL(g_plugin.getWStringA(m_hContact, "URL")); + + m_feedtitle.SetText(dbNick); + m_feedurl.SetText(dbURL); + m_checktime.SetInt(g_plugin.getDword(m_hContact, "UpdateTime", DEFAULT_UPDATE_TIME)); + + ptrW szMsgFormat(g_plugin.getWStringA(m_hContact, "MsgFormat")); + if (szMsgFormat) + m_tagedit.SetText(szMsgFormat); + + if (g_plugin.getByte(m_hContact, "UseAuth")) { + m_useauth.SetState(1); + m_login.Enable(); + m_password.Enable(); + + ptrW szLogin(g_plugin.getWStringA(m_hContact, "Login")); + if (szLogin) + m_login.SetText(szLogin); + + pass_ptrA pwd(g_plugin.getStringA(m_hContact, "Password")); + m_password.SetTextA(pwd); + } + g_arFeeds.insert(this); + Utils_RestoreWindowPositionNoSize(m_hwnd, m_hContact, MODULENAME, "ChangeDlg"); + } + return true; +} + +void CFeedEditor::OnCheckFeed(CCtrlBase*) +{ + m_checkfeed.Disable(); + m_checkfeed.SetText(TranslateT("Wait...")); + wchar_t *tszTitle = nullptr; + ptrW strfeedurl(m_feedurl.GetText()); + if (strfeedurl || mir_wstrcmp(strfeedurl, L"http://") != 0 || mir_wstrcmp(strfeedurl, L"") != 0) + tszTitle = (wchar_t*)CheckFeed(strfeedurl, this); + else + MessageBox(m_hwnd, TranslateT("Enter Feed URL"), TranslateT("Error"), MB_OK); + m_feedtitle.SetText(tszTitle); + mir_free(tszTitle); + m_checkfeed.Enable(); + m_checkfeed.SetText(TranslateT("Check Feed")); +} + +void CFeedEditor::OnReset(CCtrlBase*) +{ + if (MessageBox(m_hwnd, TranslateT("Are you sure?"), TranslateT("Tags Mask Reset"), MB_YESNO | MB_ICONWARNING) == IDYES) + m_tagedit.SetText(TAGSDEFAULT); +} + +void CFeedEditor::OnHelp(CCtrlBase*) +{ + CMStringW wszTagHelp; + wszTagHelp.Format(L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s", + L"#<title>#", TranslateT("The title of the item."), + L"#<description>#", TranslateT("The item synopsis."), + L"#<link>#", TranslateT("The URL of the item."), + L"#<author>#", TranslateT("Email address of the author of the item."), + L"#<comments>#", TranslateT("URL of a page for comments relating to the item."), + L"#<guid>#", TranslateT("A string that uniquely identifies the item."), + L"#<category>#", TranslateT("Specify one or more categories that the item belongs to.")); + MessageBox(m_hwnd, wszTagHelp, TranslateT("Feed Tag Help"), MB_OK); +} + +void CFeedEditor::OnOk(CCtrlBase*) +{ + ptrW strfeedtitle(m_feedtitle.GetText()); + if (!strfeedtitle || mir_wstrcmp(strfeedtitle, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter Feed name"), TranslateT("Error"), MB_OK); + return; + } + + ptrW strfeedurl(m_feedurl.GetText()); + if (!strfeedurl || mir_wstrcmp(strfeedurl, L"http://") == 0 || mir_wstrcmp(strfeedurl, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter Feed URL"), TranslateT("Error"), MB_OK); + return; + } + + ptrW strtagedit(m_tagedit.GetText()); + if (!strtagedit || mir_wstrcmp(strtagedit, L"") == 0) { + MessageBox(m_hwnd, TranslateT("Enter message format"), TranslateT("Error"), MB_OK); + return; + } + + MCONTACT hContact; + if (m_iItem == -1 && m_hContact == NULL) { + hContact = db_add_contact(); + Proto_AddToContact(hContact, MODULENAME); + g_plugin.setByte(hContact, "CheckState", 1); + } + else hContact = m_hContact; + + g_plugin.setWString(hContact, "Nick", strfeedtitle); + g_plugin.setWString(hContact, "URL", strfeedurl); + g_plugin.setDword(hContact, "UpdateTime", m_checktime.GetInt()); + g_plugin.setWString(hContact, "MsgFormat", strtagedit); + g_plugin.setWord(hContact, "Status", Proto_GetStatus(MODULENAME)); + if (m_useauth.IsChecked()) { + g_plugin.setByte(hContact, "UseAuth", 1); + g_plugin.setWString(hContact, "Login", m_login.GetText()); + g_plugin.setString(hContact, "Password", m_password.GetTextA()); + } + else { + g_plugin.delSetting(hContact, "UseAuth"); + g_plugin.delSetting(hContact, "Login"); + g_plugin.delSetting(hContact, "Password"); + } + + if (m_iItem == -1 && m_list != nullptr && m_hContact == NULL) { + int iItem = m_list->AddItem(strfeedtitle, -1); + m_list->SetItem(iItem, 1, strfeedurl); + m_list->SetCheckState(iItem, 1); + } + else if (m_iItem > -1) { + m_list->SetItem(m_iItem, 0, strfeedtitle); + m_list->SetItem(m_iItem, 1, strfeedurl); + } +} + +bool CFeedEditor::OnClose() +{ + g_arFeeds.remove(this); + Utils_SaveWindowPosition(m_hwnd, NULL, MODULENAME, m_iItem == -1 ? "AddDlg" : "ChangeDlg"); + if (pAddFeedDialog == this) + pAddFeedDialog = nullptr; + return true; +} + +void CFeedEditor::OnUseAuth(CCtrlBase*) +{ + m_login.Enable(m_useauth.GetState()); + m_password.Enable(m_useauth.GetState()); +} + +void COptionsMain::UpdateList() +{ + for (auto &hContact : Contacts(MODULENAME)) { + UpdateListFlag = TRUE; + ptrW ptszNick(g_plugin.getWStringA(hContact, "Nick")); + if (ptszNick) { + int iItem = m_feeds.AddItem(ptszNick, -1); + + ptrW ptszURL(g_plugin.getWStringA(hContact, "URL")); + if (ptszURL) { + m_feeds.SetItem(iItem, 1, ptszURL); + m_feeds.SetCheckState(iItem, g_plugin.getByte(hContact, "CheckState", 1)); + } + } + } + UpdateListFlag = FALSE; +} + +COptionsMain::COptionsMain() : + CDlgBase(g_plugin, IDD_OPTIONS), + m_feeds(this, IDC_FEEDLIST), + m_add(this, IDC_ADD), + m_change(this, IDC_CHANGE), + m_delete(this, IDC_REMOVE), + m_import(this, IDC_IMPORT), + m_export(this, IDC_EXPORT), + m_checkonstartup(this, IDC_STARTUPRETRIEVE) +{ + CreateLink(m_checkonstartup, "StartupRetrieve", DBVT_BYTE, 1); + + m_add.OnClick = Callback(this, &COptionsMain::OnAddButtonClick); + m_change.OnClick = Callback(this, &COptionsMain::OnChangeButtonClick); + m_delete.OnClick = Callback(this, &COptionsMain::OnDeleteButtonClick); + m_import.OnClick = Callback(this, &COptionsMain::OnImportButtonClick); + m_export.OnClick = Callback(this, &COptionsMain::OnExportButtonClick); + + m_feeds.OnItemChanged = Callback(this, &COptionsMain::OnFeedListItemChanged); + m_feeds.OnDoubleClick = Callback(this, &COptionsMain::OnFeedListDoubleClick); + +} + +bool COptionsMain::OnInitDialog() +{ + CDlgBase::OnInitDialog(); + m_change.Disable(); + m_delete.Disable(); + m_feeds.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + m_feeds.AddColumn(0, TranslateT("Feed"), 160); + m_feeds.AddColumn(1, TranslateT("URL"), 276); + UpdateList(); + return true; +} + +bool COptionsMain::OnApply() +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + for (int i = 0; i < m_feeds.GetItemCount(); i++) { + wchar_t nick[MAX_PATH]; + m_feeds.GetItemText(i, 0, nick, _countof(nick)); + if (mir_wstrcmp(dbNick, nick) == 0) { + g_plugin.setByte(hContact, "CheckState", m_feeds.GetCheckState(i)); + if (!m_feeds.GetCheckState(i)) + db_set_b(hContact, "CList", "Hidden", 1); + else + db_unset(hContact, "CList", "Hidden"); + } + } + } + return true; +} + +void COptionsMain::OnAddButtonClick(CCtrlBase*) +{ + if (pAddFeedDialog == nullptr) { + pAddFeedDialog = new CFeedEditor(-1, &m_feeds, NULL); + pAddFeedDialog->SetParent(m_hwnd); + pAddFeedDialog->Show(); + } + else { + SetForegroundWindow(pAddFeedDialog->GetHwnd()); + SetFocus(pAddFeedDialog->GetHwnd()); + } +} + +void COptionsMain::OnChangeButtonClick(CCtrlBase*) +{ + int isel = m_feeds.GetSelectionMark(); + CFeedEditor *pDlg = nullptr; + for (auto &it : g_arFeeds) { + wchar_t nick[MAX_PATH], url[MAX_PATH]; + m_feeds.GetItemText(isel, 0, nick, _countof(nick)); + m_feeds.GetItemText(isel, 1, url, _countof(url)); + + ptrW dbNick(g_plugin.getWStringA(it->getContact(), "Nick")); + if ((dbNick == NULL) || (mir_wstrcmp(dbNick, nick) != 0)) + continue; + + ptrW dbURL(g_plugin.getWStringA(it->getContact(), "URL")); + if ((dbURL == NULL) || (mir_wstrcmp(dbURL, url) != 0)) + continue; + + pDlg = it; + } + + if (pDlg == nullptr) { + pDlg = new CFeedEditor(isel, &m_feeds, NULL); + pDlg->SetParent(m_hwnd); + pDlg->Show(); + } + else { + SetForegroundWindow(pDlg->GetHwnd()); + SetFocus(pDlg->GetHwnd()); + } +} + +void COptionsMain::OnDeleteButtonClick(CCtrlBase*) +{ + if (MessageBox(m_hwnd, TranslateT("Are you sure?"), TranslateT("Contact deleting"), MB_YESNO | MB_ICONWARNING) == IDYES) { + wchar_t nick[MAX_PATH], url[MAX_PATH]; + int isel = m_feeds.GetSelectionMark(); + m_feeds.GetItemText(isel, 0, nick, _countof(nick)); + m_feeds.GetItemText(isel, 1, url, _countof(url)); + + for (auto &hContact : Contacts(MODULENAME)) { + ptrW dbNick(g_plugin.getWStringA(hContact, "Nick")); + if (dbNick == NULL) + break; + if (mir_wstrcmp(dbNick, nick)) + continue; + + ptrW dbURL(g_plugin.getWStringA(hContact, "URL")); + if (dbURL == NULL) + break; + if (mir_wstrcmp(dbURL, url)) + continue; + + db_delete_contact(hContact); + m_feeds.DeleteItem(isel); + break; + } + } +} + +void COptionsMain::OnImportButtonClick(CCtrlBase*) +{ + if (pImportDialog == nullptr) { + pImportDialog = new CImportFeed(&m_feeds); + pImportDialog->Show(); + pImportDialog->SetParent(m_hwnd); + } +} + +void COptionsMain::OnExportButtonClick(CCtrlBase*) +{ + if (pExportDialog == nullptr) { + pExportDialog = new CExportFeed(); + pExportDialog->Show(); + pExportDialog->SetParent(m_hwnd); + } +} + +void COptionsMain::OnFeedListItemChanged(CCtrlListView::TEventInfo *evt) +{ + int isel = m_feeds.GetSelectionMark(); + if (isel == -1) { + m_change.Disable(); + m_delete.Disable(); + } + else { + m_change.Enable(); + m_delete.Enable(); + } + if (((evt->nmlv->uNewState ^ evt->nmlv->uOldState) & LVIS_STATEIMAGEMASK) && !UpdateListFlag) + NotifyChange(); +} + +void COptionsMain::OnFeedListDoubleClick(CCtrlBase*) +{ + int isel = m_feeds.GetHotItem(); + if (isel != -1) { + CFeedEditor *pDlg = new CFeedEditor(isel, &m_feeds, 0); + pDlg->SetParent(m_hwnd); + pDlg->Show(); + } +} + +int OptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; + odp.szGroup.w = LPGENW("Network"); + odp.szTitle.w = LPGENW("News Aggregator"); + odp.pDialog = new COptionsMain(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Options.h b/protocols/NewsAggregator/Src/Options.h new file mode 100644 index 0000000000..2389eb9bb9 --- /dev/null +++ b/protocols/NewsAggregator/Src/Options.h @@ -0,0 +1,162 @@ +#ifndef _OPTIONS_H_ +#define _OPTIONS_H_ + +class COptionsMain : public CDlgBase +{ +private: + CCtrlListView m_feeds; + CCtrlButton m_add; + CCtrlButton m_change; + CCtrlButton m_delete; + CCtrlButton m_import; + CCtrlButton m_export; + CCtrlCheck m_checkonstartup; + +protected: + bool OnInitDialog() override; + bool OnApply() override; + + void OnAddButtonClick(CCtrlBase*); + void OnChangeButtonClick(CCtrlBase*); + void OnDeleteButtonClick(CCtrlBase*); + void OnImportButtonClick(CCtrlBase*); + void OnExportButtonClick(CCtrlBase*); + + void OnFeedListItemChanged(CCtrlListView::TEventInfo *evt); + void OnFeedListDoubleClick(CCtrlBase*); + + void UpdateList(); + +public: + COptionsMain(); +}; + +class CFeedEditor : public CDlgBase +{ + friend class CAuthRequest; + +private: + typedef CDlgBase CSuper; + + int m_iItem; + CCtrlListView *m_list; + MCONTACT m_hContact; + + CCtrlEdit m_feedtitle; + CCtrlEdit m_feedurl; + CCtrlEdit m_checktime; + CCtrlSpin m_checktimespin; + CCtrlButton m_checkfeed; + CCtrlEdit m_tagedit; + CCtrlButton m_reset; + CCtrlButton m_help; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnCheckFeed(CCtrlBase*); + void OnReset(CCtrlBase*); + void OnHelp(CCtrlBase*); + void OnOk(CCtrlBase*); + void OnUseAuth(CCtrlBase*); + +public: + CCtrlCheck m_useauth; + CCtrlEdit m_login; + CCtrlEdit m_password; + + CFeedEditor(int iItem, CCtrlListView *m_list, MCONTACT Contact); + + __inline MCONTACT getContact() const { return m_hContact; } +}; + +class CImportFeed : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CCtrlListView *m_list; + + CCtrlEdit m_importfile; + CCtrlButton m_browsefile; + CCtrlListBox m_feedslist; + CCtrlListBox m_feedsimportlist; + CCtrlButton m_addfeed; + CCtrlButton m_removefeed; + CCtrlButton m_addallfeeds; + CCtrlButton m_removeallfeeds; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnBrowseFile(CCtrlBase*); + void OnAddFeed(CCtrlBase*); + void OnRemoveFeed(CCtrlBase*); + void OnAddAllFeeds(CCtrlBase*); + void OnRemoveAllFeeds(CCtrlBase*); + void OnOk(CCtrlBase*); + + void OnFeedsList(CCtrlBase*); + void OnFeedsImportList(CCtrlBase*); + +public: + CImportFeed(CCtrlListView *m_list); +}; + +class CExportFeed : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CCtrlListBox m_feedslist; + CCtrlListBox m_feedsexportlist; + CCtrlButton m_addfeed; + CCtrlButton m_removefeed; + CCtrlButton m_addallfeeds; + CCtrlButton m_removeallfeeds; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + bool OnClose() override; + + void OnAddFeed(CCtrlBase*); + void OnRemoveFeed(CCtrlBase*); + void OnAddAllFeeds(CCtrlBase*); + void OnRemoveAllFeeds(CCtrlBase*); + void OnOk(CCtrlBase*); + + void OnFeedsList(CCtrlBase*); + void OnFeedsExportList(CCtrlBase*); + +public: + CExportFeed(); +}; + +class CAuthRequest : public CDlgBase +{ +private: + typedef CDlgBase CSuper; + + CFeedEditor *m_pDlg; + MCONTACT m_hContact; + + CCtrlBase m_feedname; + CCtrlEdit m_username; + CCtrlEdit m_password; + CCtrlButton m_ok; + +protected: + bool OnInitDialog() override; + + void OnOk(CCtrlBase*); + +public: + CAuthRequest(CFeedEditor *pDlg, MCONTACT hContact); +}; + +#endif //_OPTIONS_H_
\ No newline at end of file diff --git a/protocols/NewsAggregator/Src/Services.cpp b/protocols/NewsAggregator/Src/Services.cpp new file mode 100644 index 0000000000..b5b8c9f028 --- /dev/null +++ b/protocols/NewsAggregator/Src/Services.cpp @@ -0,0 +1,261 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +int g_nStatus = ID_STATUS_ONLINE; +UINT_PTR timerId = 0; +HANDLE hTBButton = nullptr, hNewsAggregatorFolder = nullptr; + +int OnFoldersChanged(WPARAM, LPARAM) +{ + FoldersGetCustomPathT(hNewsAggregatorFolder, tszRoot, MAX_PATH, L""); + return 0; +} + +int NewsAggrInit(WPARAM, LPARAM) +{ + if (hNewsAggregatorFolder = FoldersRegisterCustomPathT(LPGEN("Avatars"), LPGEN("News Aggregator"), MIRANDA_USERDATAT L"\\Avatars\\" _A2W(DEFAULT_AVATARS_FOLDER))) + FoldersGetCustomPathT(hNewsAggregatorFolder, tszRoot, MAX_PATH, L""); + else + mir_wstrncpy(tszRoot, VARSW(L"%miranda_userdata%\\Avatars\\" _A2W(DEFAULT_AVATARS_FOLDER)), _countof(tszRoot)); + + for (auto &hContact : Contacts(MODULENAME)) { + if (!g_plugin.getByte("StartupRetrieve", 1)) + g_plugin.setDword(hContact, "LastCheck", (DWORD)time(0)); + g_plugin.setWord(hContact, "Status", ID_STATUS_ONLINE); + } + + NetlibInit(); + InitMenu(); + + HookEvent(ME_TTB_MODULELOADED, OnToolbarLoaded); + HookEvent(ME_FOLDERS_PATH_CHANGED, OnFoldersChanged); + + // timer for the first update + timerId = SetTimer(nullptr, 0, 10000, timerProc2); // first update is 10 sec after load + + return 0; +} + +int NewsAggrPreShutdown(WPARAM, LPARAM) +{ + KillTimer(nullptr, timerId); + NetlibUnInit(); + return 0; +} + +INT_PTR NewsAggrGetName(WPARAM wParam, LPARAM lParam) +{ + if(lParam) { + mir_strncpy((char *)lParam, MODULENAME, wParam); + return 0; + } + + return 1; +} + +INT_PTR NewsAggrGetCaps(WPARAM wp, LPARAM) +{ + switch(wp) { + case PFLAGNUM_1: + return PF1_IM | PF1_PEER2PEER; + case PFLAGNUM_3: + case PFLAGNUM_2: + return PF2_ONLINE; + case PFLAGNUM_4: + return PF4_AVATARS; + case PFLAG_UNIQUEIDTEXT: + return (INT_PTR) "News Feed"; + default: + return 0; + } +} + +INT_PTR NewsAggrSetStatus(WPARAM wp, LPARAM) +{ + int nStatus = (int)wp; + if ((ID_STATUS_ONLINE == nStatus) || (ID_STATUS_OFFLINE == nStatus)) { + int nOldStatus = g_nStatus; + if(nStatus != g_nStatus) { + g_nStatus = nStatus; + + for (auto &hContact : Contacts(MODULENAME)) + g_plugin.setWord(hContact, "Status", nStatus); + + ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)nOldStatus, (LPARAM)g_nStatus); + } + } + + return 0; +} + +INT_PTR NewsAggrGetStatus(WPARAM, LPARAM) +{ + return g_nStatus; +} + +INT_PTR NewsAggrLoadIcon(WPARAM wParam, LPARAM) +{ + return (LOWORD(wParam) == PLI_PROTOCOL) ? (INT_PTR)CopyIcon(LoadIconEx("main", FALSE)) : 0; +} + +static void __cdecl AckThreadProc(void *param) +{ + Sleep(100); + ProtoBroadcastAck(MODULENAME, (MCONTACT)param, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1); +} + +INT_PTR NewsAggrGetInfo(WPARAM, LPARAM lParam) +{ + CCSDATA *ccs = (CCSDATA *)lParam; + mir_forkthread(AckThreadProc, (void*)ccs->hContact); + return 0; +} + +INT_PTR CheckAllFeeds(WPARAM, LPARAM lParam) +{ + for (auto &hContact : Contacts(MODULENAME)) { + if (lParam && g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) + UpdateListAdd(hContact); + else if (!lParam) + UpdateListAdd(hContact); + } + if (!ThreadRunning) + mir_forkthread(UpdateThreadProc); + + return 0; +} + +INT_PTR AddFeed(WPARAM, LPARAM) +{ + if (pAddFeedDialog == nullptr) { + pAddFeedDialog = new CFeedEditor(-1, nullptr, NULL); + pAddFeedDialog->Show(); + } + else { + SetForegroundWindow(pAddFeedDialog->GetHwnd()); + SetFocus(pAddFeedDialog->GetHwnd()); + } + return 0; +} + +INT_PTR ChangeFeed(WPARAM hContact, LPARAM) +{ + CFeedEditor *pDlg = nullptr; + for (auto &it : g_arFeeds) + if (it->getContact() == hContact) + pDlg = it; + + if (pDlg == nullptr) { + pDlg = new CFeedEditor(-1, nullptr, (MCONTACT)hContact); + pDlg->Show(); + } + else { + SetForegroundWindow(pDlg->GetHwnd()); + SetFocus(pDlg->GetHwnd()); + } + return 0; +} + +INT_PTR ImportFeeds(WPARAM, LPARAM) +{ + if (pImportDialog == nullptr) + pImportDialog = new CImportFeed(nullptr); + pImportDialog->Show(); + return 0; +} + +INT_PTR ExportFeeds(WPARAM, LPARAM) +{ + if (pExportDialog == nullptr) + pExportDialog = new CExportFeed(); + pExportDialog->Show(); + return 0; +} + +INT_PTR CheckFeed(WPARAM hContact, LPARAM) +{ + if(IsMyContact((MCONTACT)hContact)) + UpdateListAdd((MCONTACT)hContact); + if ( !ThreadRunning) + mir_forkthread(UpdateThreadProc); + return 0; +} + +INT_PTR NewsAggrGetAvatarInfo(WPARAM wParam, LPARAM lParam) +{ + PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam; + if (!IsMyContact(pai->hContact)) + return GAIR_NOAVATAR; + + // if GAIF_FORCE is set, we are updating the feed + // otherwise, cached avatar is used + if ((wParam & GAIF_FORCE) && g_plugin.getDword(pai->hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) + UpdateListAdd(pai->hContact); + if (g_plugin.getByte("AutoUpdate", 1) != 0 && !ThreadRunning) + mir_forkthread(UpdateThreadProc, (void *)TRUE); + + ptrW ptszImageURL(g_plugin.getWStringA(pai->hContact, "ImageURL")); + return (ptszImageURL == nullptr) ? GAIR_NOAVATAR : GAIR_WAITFOR; +} + +INT_PTR NewsAggrRecvMessage(WPARAM, LPARAM lParam) +{ + PROTOACCOUNT *pa = Proto_GetAccount(MODULENAME); + if (pa && pa->ppro) { + CCSDATA *ccs = (CCSDATA*)lParam; + pa->ppro->PROTO_INTERFACE::RecvMsg(ccs->hContact, (PROTORECVEVENT*)ccs->lParam); + } + + return 0; +} + +void UpdateMenu(bool State) +{ + if (!State) // to enable auto-update + Menu_ModifyItem(hService2[0], LPGENW("Auto Update Enabled"), GetIconHandle("enabled")); + else // to disable auto-update + Menu_ModifyItem(hService2[0], LPGENW("Auto Update Disabled"), GetIconHandle("disabled")); + + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTBButton, State ? TTBST_PUSHED : 0); + g_plugin.setByte("AutoUpdate", !State); +} + +// update the newsaggregator auto-update menu item when click on it +INT_PTR EnableDisable(WPARAM, LPARAM) +{ + UpdateMenu(g_plugin.getByte("AutoUpdate", 1) != 0); + NewsAggrSetStatus(g_plugin.getByte("AutoUpdate", 1) ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE, 0); + return 0; +} + +int OnToolbarLoaded(WPARAM, LPARAM) +{ + TTBButton ttb = {}; + ttb.name = LPGEN("Enable/disable auto update"); + ttb.pszService = MS_NEWSAGGREGATOR_ENABLED; + ttb.pszTooltipUp = LPGEN("Auto Update Enabled"); + ttb.pszTooltipDn = LPGEN("Auto Update Disabled"); + ttb.hIconHandleUp = GetIconHandle("enabled"); + ttb.hIconHandleDn = GetIconHandle("disabled"); + ttb.dwFlags = (g_plugin.getByte("AutoUpdate", 1) ? 0 : TTBBF_PUSHED) | TTBBF_ASPUSHBUTTON | TTBBF_VISIBLE; + hTBButton = g_plugin.addTTB(&ttb); + return 0; +} diff --git a/protocols/NewsAggregator/Src/Update.cpp b/protocols/NewsAggregator/Src/Update.cpp new file mode 100644 index 0000000000..96990dbcd2 --- /dev/null +++ b/protocols/NewsAggregator/Src/Update.cpp @@ -0,0 +1,139 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +// check if Feed is currently updating +bool ThreadRunning; +UPDATELIST *UpdateListHead = nullptr; +UPDATELIST *UpdateListTail = nullptr; + +// main auto-update timer +void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD) +{ + // only run if it is not current updating and the auto update option is enabled + if (!ThreadRunning && !Miranda_IsTerminated()) { + bool HaveUpdates = FALSE; + for (auto &hContact : Contacts(MODULENAME)) { + if (g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME)) { + double diff = difftime(time(0), (time_t)g_plugin.getDword(hContact, "LastCheck", 0)); + if (g_plugin.getByte("AutoUpdate", 1) != 0 && diff >= g_plugin.getDword(hContact, "UpdateTime", DEFAULT_UPDATE_TIME) * 60) { + UpdateListAdd(hContact); + HaveUpdates = TRUE; + } + } + } + if (!ThreadRunning && HaveUpdates) + mir_forkthread(UpdateThreadProc); + } +} + +// temporary timer for first run +// when this is run, it kill the old startup timer and create the permenant one above +void CALLBACK timerProc2(HWND, UINT, UINT_PTR, DWORD) +{ + KillTimer(nullptr, timerId); + ThreadRunning = FALSE; + + if (g_plugin.getByte("AutoUpdate", 1) && !Miranda_IsTerminated()) { + if (g_plugin.getByte("StartupRetrieve", 1)) + CheckAllFeeds(0, 1); + timerId = SetTimer(nullptr, 0, 30000, (TIMERPROC)timerProc); + } +} + +void UpdateListAdd(MCONTACT hContact) +{ + UPDATELIST *newItem = (UPDATELIST*)mir_alloc(sizeof(UPDATELIST)); + newItem->hContact = hContact; + newItem->next = nullptr; + + WaitForSingleObject(hUpdateMutex, INFINITE); + + if (UpdateListTail == nullptr) + UpdateListHead = newItem; + else UpdateListTail->next = newItem; + UpdateListTail = newItem; + + ReleaseMutex(hUpdateMutex); +} + +MCONTACT UpdateGetFirst() +{ + MCONTACT hContact = NULL; + + WaitForSingleObject(hUpdateMutex, INFINITE); + + if (UpdateListHead != nullptr) { + UPDATELIST* Item = UpdateListHead; + hContact = Item->hContact; + UpdateListHead = Item->next; + mir_free(Item); + + if (UpdateListHead == nullptr) + UpdateListTail = nullptr; + } + + ReleaseMutex(hUpdateMutex); + + return hContact; +} + +void DestroyUpdateList(void) +{ + WaitForSingleObject(hUpdateMutex, INFINITE); + + // free the list one by one + UPDATELIST *temp = UpdateListHead; + while (temp != nullptr) { + UpdateListHead = temp->next; + mir_free(temp); + temp = UpdateListHead; + } + // make sure the entire list is clear + UpdateListTail = nullptr; + + ReleaseMutex(hUpdateMutex); +} + +void UpdateThreadProc(void *AvatarCheck) +{ + WaitForSingleObject(hUpdateMutex, INFINITE); + if (ThreadRunning) { + ReleaseMutex(hUpdateMutex); + return; + } + ThreadRunning = TRUE; // prevent 2 instance of this thread running + ReleaseMutex(hUpdateMutex); + + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + // update news by getting the first station from the queue until the queue is empty + while (UpdateListHead != nullptr && !Miranda_IsTerminated()) { + if (AvatarCheck != nullptr) + CheckCurrentFeedAvatar(UpdateGetFirst()); + else + CheckCurrentFeed(UpdateGetFirst()); + } + + // exit the update thread + ThreadRunning = FALSE; + + CoUninitialize(); +} diff --git a/protocols/NewsAggregator/Src/Utils.cpp b/protocols/NewsAggregator/Src/Utils.cpp new file mode 100644 index 0000000000..80248ba48f --- /dev/null +++ b/protocols/NewsAggregator/Src/Utils.cpp @@ -0,0 +1,445 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include "stdafx.h" + +HNETLIBUSER hNetlibUser = nullptr; +HNETLIBCONN hNetlibHttp; +bool UpdateListFlag = FALSE; + +bool IsMyContact(MCONTACT hContact) +{ + const char *szProto = GetContactProto(hContact); + return szProto != nullptr && mir_strcmp(MODULENAME, szProto) == 0; +} + +void NetlibInit() +{ + NETLIBUSER nlu = {}; + nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; + nlu.szDescriptiveName.w = TranslateT("NewsAggregator HTTP connections"); + nlu.szSettingsModule = MODULENAME; + hNetlibUser = Netlib_RegisterUser(&nlu); +} + +void NetlibUnInit() +{ + Netlib_CloseHandle(hNetlibUser); + hNetlibUser = nullptr; +} + +void GetNewsData(wchar_t *tszUrl, char **szData, MCONTACT hContact, CFeedEditor *pEditDlg) +{ + Netlib_LogfW(hNetlibUser, L"Getting feed data %s.", tszUrl); + NETLIBHTTPREQUEST nlhr = { 0 }; + + // initialize the netlib request + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11 | NLHRF_REDIRECT; + if (wcsstr(tszUrl, L"https://") != nullptr) + nlhr.flags |= NLHRF_SSL; + char *szUrl = mir_u2a(tszUrl); + nlhr.szUrl = szUrl; + nlhr.nlc = hNetlibHttp; + + // change the header so the plugin is pretended to be IE 6 + WinXP + NETLIBHTTPHEADER headers[5]; + nlhr.headersCount = 4; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = NETLIB_USER_AGENT; + nlhr.headers[1].szName = "Cache-Control"; + nlhr.headers[1].szValue = "no-cache"; + nlhr.headers[2].szName = "Pragma"; + nlhr.headers[2].szValue = "no-cache"; + nlhr.headers[3].szName = "Connection"; + nlhr.headers[3].szValue = "close"; + char auth[256]; + if (g_plugin.getByte(hContact, "UseAuth", 0) || (pEditDlg && pEditDlg->m_useauth.IsChecked()) /*IsDlgButtonChecked(hwndDlg, IDC_USEAUTH)*/) { + nlhr.headersCount++; + nlhr.headers[4].szName = "Authorization"; + + CreateAuthString(auth, hContact, pEditDlg); + nlhr.headers[4].szValue = auth; + } + + // download the page + NETLIBHTTPREQUEST *nlhrReply = Netlib_HttpTransaction(hNetlibUser, &nlhr); + if (nlhrReply) { + // if the recieved code is 200 OK + if (nlhrReply->resultCode == 200 && nlhrReply->dataLength > 0) { + Netlib_LogfW(hNetlibUser, L"Code 200: Succeeded getting feed data %s.", tszUrl); + // allocate memory and save the retrieved data + *szData = (char *)mir_alloc((size_t)(nlhrReply->dataLength + 2)); + memcpy(*szData, nlhrReply->pData, (size_t)nlhrReply->dataLength); + (*szData)[nlhrReply->dataLength] = 0; + } + else if (nlhrReply->resultCode == 401) { + Netlib_LogfW(hNetlibUser, L"Code 401: feed %s needs auth data.", tszUrl); + + if (CAuthRequest(pEditDlg, hContact).DoModal() == IDOK) + GetNewsData(tszUrl, szData, hContact, pEditDlg); + } + else Netlib_LogfW(hNetlibUser, L"Code %d: Failed getting feed data %s.", nlhrReply->resultCode, tszUrl); + + Netlib_FreeHttpRequest(nlhrReply); + } + else Netlib_LogfW(hNetlibUser, L"Failed getting feed data %s, no response.", tszUrl); + + mir_free(szUrl); +} + +time_t DateToUnixTime(const char *stamp, bool FeedType) +{ + struct tm timestamp; + char date[9]; + int i, y; + time_t t; + + if (stamp == nullptr) + return 0; + + char *p = NEWSTR_ALLOCA(stamp); + + if (FeedType) { + // skip '-' chars + int si = 0, sj = 0; + while (true) { + if (p[si] == '-') + si++; + else if (!(p[sj++] = p[si++])) + break; + } + } + else { + char monthstr[4], timezonesign[2]; + int day, month = 0, year, hour, min, sec, timezoneh, timezonem; + if (strchr(p, ',')) { + strtok(p, ","); + p = strtok(nullptr, ","); + sscanf(p + 1, "%d %3s %d %d:%d:%d %1s%02d%02d", &day, &monthstr, &year, &hour, &min, &sec, &timezonesign, &timezoneh, &timezonem); + if (!mir_strcmpi(monthstr, "Jan")) + month = 1; + if (!mir_strcmpi(monthstr, "Feb")) + month = 2; + if (!mir_strcmpi(monthstr, "Mar")) + month = 3; + if (!mir_strcmpi(monthstr, "Apr")) + month = 4; + if (!mir_strcmpi(monthstr, "May")) + month = 5; + if (!mir_strcmpi(monthstr, "Jun")) + month = 6; + if (!mir_strcmpi(monthstr, "Jul")) + month = 7; + if (!mir_strcmpi(monthstr, "Aug")) + month = 8; + if (!mir_strcmpi(monthstr, "Sep")) + month = 9; + if (!mir_strcmpi(monthstr, "Oct")) + month = 10; + if (!mir_strcmpi(monthstr, "Nov")) + month = 11; + if (!mir_strcmpi(monthstr, "Dec")) + month = 12; + if (year < 2000) + year += 2000; + if (!mir_strcmp(timezonesign, "+")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour - timezoneh, min - timezonem, sec); + else if (!mir_strcmp(timezonesign, "-")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour + timezoneh, min + timezonem, sec); + else + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + else if (strchr(p, 'T')) { + sscanf(p, "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &min, &sec); + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + else { + sscanf(p, "%d-%d-%d %d:%d:%d %1s%02d%02d", &year, &month, &day, &hour, &min, &sec, &timezonesign, &timezoneh, &timezonem); + if (!mir_strcmp(timezonesign, "+")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour - timezoneh, min - timezonem, sec); + else if (!mir_strcmp(timezonesign, "-")) + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour + timezoneh, min + timezonem, sec); + else + mir_snprintf(p, 4 + 2 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1, "%04d%02d%02dT%02d:%02d:%02d", year, month, day, hour, min, sec); + } + } + // Get the date part + for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++) + date[i] = *p; + + // Parse year + if (i == 6) { + // 2-digit year ( 1970-2069 ) + y = (date[0] - '0') * 10 + (date[1] - '0'); + if (y < 70) + y += 100; + } + else if (i == 8) { + // 4-digit year + y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0'; + y -= 1900; + } + else return 0; + + timestamp.tm_year = y; + // Parse month + timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1; + // Parse date + timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0'; + + // Skip any date/time delimiter + for (; *p != '\0' && !isdigit(*p); p++); + + // Parse time + if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3) + return 0; + + timestamp.tm_isdst = 0; // DST is already present in _timezone below + t = mktime(×tamp); + + _tzset(); + t -= (time_t)_timezone; + return (t >= 0) ? t : 0; +} + +bool DownloadFile(LPCTSTR tszURL, LPCTSTR tszLocal) +{ + NETLIBHTTPREQUEST nlhr = { 0 }; + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11; + char *szUrl = mir_u2a(tszURL); + nlhr.szUrl = szUrl; + NETLIBHTTPHEADER headers[4]; + nlhr.headersCount = 4; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = NETLIB_USER_AGENT; + nlhr.headers[1].szName = "Connection"; + nlhr.headers[1].szValue = "close"; + nlhr.headers[2].szName = "Cache-Control"; + nlhr.headers[2].szValue = "no-cache"; + nlhr.headers[3].szName = "Pragma"; + nlhr.headers[3].szValue = "no-cache"; + + bool ret = false; + NETLIBHTTPREQUEST *pReply = Netlib_HttpTransaction(hNetlibUser, &nlhr); + if (pReply) { + if ((200 == pReply->resultCode) && (pReply->dataLength > 0)) { + char *date = nullptr, *size = nullptr; + for (int i = 0; i < pReply->headersCount; i++) { + if (!mir_strcmpi(pReply->headers[i].szName, "Last-Modified")) { + date = pReply->headers[i].szValue; + continue; + } + else if (!mir_strcmpi(pReply->headers[i].szName, "Content-Length")) { + size = pReply->headers[i].szValue; + continue; + } + } + if (date != nullptr && size != nullptr) { + wchar_t *tsize = mir_a2u(size); + struct _stat buf; + + int fh = _wopen(tszLocal, _O_RDONLY); + if (fh != -1) { + _fstat(fh, &buf); + time_t modtime = DateToUnixTime(date, 0); + time_t filemodtime = mktime(localtime(&buf.st_atime)); + if (modtime > filemodtime && buf.st_size != _wtoi(tsize)) { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + _close(fh); + } + else { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + mir_free(tsize); + } + else { + DWORD dwBytes; + HANDLE hFile = CreateFile(tszLocal, GENERIC_READ | GENERIC_WRITE, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + WriteFile(hFile, pReply->pData, (DWORD)pReply->dataLength, &dwBytes, nullptr); + ret = true; + if (hFile) + CloseHandle(hFile); + } + } + Netlib_FreeHttpRequest(pReply); + } + + mir_free(szUrl); + + return ret; +} + +typedef HRESULT(MarkupCallback)(IHTMLDocument3 *, BSTR &message); + +HRESULT TestMarkupServices(BSTR bstrHtml, MarkupCallback *pCallback, BSTR &message) +{ + IHTMLDocument3 *pHtmlDocRoot = nullptr; + + // Create the root document -- a "workspace" for parsing. + HRESULT hr = CoCreateInstance(CLSID_HTMLDocument, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pHtmlDocRoot)); + if (SUCCEEDED(hr) && pHtmlDocRoot) { + IPersistStreamInit *pPersistStreamInit = nullptr; + + hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pPersistStreamInit)); + if (SUCCEEDED(hr)) { + // Initialize the root document to a default state -- ready for parsing. + pPersistStreamInit->InitNew(); + + IMarkupServices *pMarkupServices = nullptr; + hr = pHtmlDocRoot->QueryInterface(IID_PPV_ARGS(&pMarkupServices)); + if (SUCCEEDED(hr)) { + IMarkupPointer *pMarkupBegin = nullptr; + IMarkupPointer *pMarkupEnd = nullptr; + + // These markup pointers indicate the insertion point. + hr = pMarkupServices->CreateMarkupPointer(&pMarkupBegin); + if (SUCCEEDED(hr)) + hr = pMarkupServices->CreateMarkupPointer(&pMarkupEnd); + + if (SUCCEEDED(hr) && pMarkupBegin && pMarkupEnd) { + IMarkupContainer *pMarkupContainer = nullptr; + + // Parse the string -- the markup container contains the parsed HTML. + // Markup pointers are updated to point to begining and end of new container. + hr = pMarkupServices->ParseString(bstrHtml, 0, &pMarkupContainer, pMarkupBegin, pMarkupEnd); + if (SUCCEEDED(hr) && pMarkupContainer) { + IHTMLDocument3 *pHtmlDoc = nullptr; + + // Retrieve the document interface to the markup container. + hr = pMarkupContainer->QueryInterface(IID_PPV_ARGS(&pHtmlDoc)); + if (SUCCEEDED(hr) && pHtmlDoc) { + // Invoke the user-defined action for this new fragment. + hr = pCallback(pHtmlDoc, message); + + // Clean up. + pHtmlDoc->Release(); + } + pMarkupContainer->Release(); + } + pMarkupEnd->Release(); + } + if (pMarkupBegin) + pMarkupBegin->Release(); + pMarkupServices->Release(); + } + pPersistStreamInit->Release(); + } + pHtmlDocRoot->Release(); + } + return hr; +} + +HRESULT TestDocumentText(IHTMLDocument3 *pHtmlDoc, BSTR &message) +{ + IHTMLDocument2 *pDoc = nullptr; + IHTMLElement *pElem = nullptr; + BSTR bstrId = SysAllocString(L"test"); + + HRESULT hr = pHtmlDoc->QueryInterface(IID_PPV_ARGS(&pDoc)); + if (SUCCEEDED(hr) && pDoc) { + hr = pDoc->get_body(&pElem); + if (SUCCEEDED(hr) && pElem) { + BSTR bstrText = nullptr; + pElem->get_innerText(&bstrText); + message = SysAllocString(bstrText); + SysFreeString(bstrText); + pElem->Release(); + } + + pDoc->Release(); + } + + SysFreeString(bstrId); + return hr; +} + +LPCTSTR ClearText(CMStringW &result, const wchar_t *message) +{ + BSTR bstrHtml = SysAllocString(message), bstrRes = SysAllocString(L""); + HRESULT hr = TestMarkupServices(bstrHtml, &TestDocumentText, bstrRes); + if (SUCCEEDED(hr)) + result = bstrRes; + else + result = message; + SysFreeString(bstrHtml); + SysFreeString(bstrRes); + + result.Replace(L"£", L"£"); //pound + result.Replace(L"²", L"²"); //sup2 + result.Replace(L"ä", L"ä"); //auml + result.Replace(L"é", L"é"); //latin small letter e with acute + result.Replace(L"ë", L"ë"); //euml + result.Replace(L"ö", L"ö"); //ouml + result.Replace(L"ž", L"ž"); //Latin Small Letter Z With Caron + result.Replace(L"ʙ", L"ʙ"); //latin letter small capital b + result.Replace(L"̆", L"˘"); //Combining Breve + result.Replace(L"́", L"´"); //Combining Acute Accent острое ударение + result.Replace(L"ο", L"ό"); // greek small letter omicron with tonos + result.Replace(L"ѣ", L"ѣ"); //Cyrillic Small Letter Yat + result.Replace(L"Ҝ", L"Ҝ"); //cyrillic capital letter ka with vertical stroke + result.Replace(L"​", L""); + result.Replace(L"‎", L""); //lrm + result.Replace(L"‏", L""); //rlm + result.Replace(L"‑", L"‑"); //Non-Breaking Hyphen + result.Replace(L"‣", L"‣"); //Triangular Bullet + result.Replace(L"−", L"−"); //minus + result.Replace(L"☺", L"☺"); //White Smiling Face + result.Replace(L"", L""); + + result.Trim(); + + return result; +} + +MCONTACT GetContactByNick(const wchar_t *nick) +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW contactNick(g_plugin.getWStringA(hContact, "Nick")); + if (!mir_wstrcmpi(contactNick, nick)) + return hContact; + } + return 0; +} + +MCONTACT GetContactByURL(const wchar_t *url) +{ + for (auto &hContact : Contacts(MODULENAME)) { + ptrW contactURL(g_plugin.getWStringA(hContact, "URL")); + if (!mir_wstrcmpi(contactURL, url)) + return hContact; + } + return 0; +} diff --git a/protocols/NewsAggregator/Src/resource.h b/protocols/NewsAggregator/Src/resource.h new file mode 100644 index 0000000000..131a0844ba --- /dev/null +++ b/protocols/NewsAggregator/Src/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by c:\Temp\Miranda NG\plugins\NewsAggregator\res\Resource.rc +// +#define IDD_OPTIONS 101 +#define IDD_AUTHENTICATION 102 +#define IDI_ICON 109 +#define IDD_ADDFEED 110 +#define IDI_CHECKALL 111 +#define IDI_ADDFEED 112 +#define IDI_IMPORTFEEDS 113 +#define IDI_EXPORTFEEDS 114 +#define IDI_ENABLED 115 +#define IDI_DISABLED 116 +#define IDD_FEEDEXPORT 140 +#define IDD_FEEDIMPORT 141 +#define IDC_TIMEOUT_VALUE_SPIN 1035 +#define IDC_FEEDLIST 1036 +#define IDC_ADD 1037 +#define IDC_CHANGE 1038 +#define IDC_REMOVE 1039 +#define IDC_IMPORT 1040 +#define IDC_EXPORT 1041 +#define IDC_FEEDTITLE 1042 +#define IDC_FEEDURL 1043 +#define IDC_CHECKTIME 1044 +#define IDC_DISCOVERY 1045 +#define IDC_USEAUTH 1046 +#define IDC_LOGIN 1047 +#define IDC_PASSWORD 1048 +#define IDC_TAGSEDIT 1049 +#define IDC_RESET 1050 +#define IDC_TAGHELP 1051 +#define IDC_STARTUPRETRIEVE 1052 +#define IDC_FEEDUSERNAME 1105 +#define IDC_FEEDPASSWORD 1106 +#define IDC_FEEDSLIST 1108 +#define IDC_FEEDSEXPORTLIST 1109 +#define IDC_ADDFEED 1110 +#define IDC_REMOVEFEED 1111 +#define IDC_ADDALLFEEDS 1112 +#define IDC_REMOVEALLFEEDS 1113 +#define IDC_IMPORTFILEPATH 1114 +#define IDC_BROWSEIMPORTFILE 1115 +#define IDC_FEEDSIMPORTLIST 1117 +#define IDC_FEEDNAME 1124 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_COMMAND_VALUE 40075 +#define _APS_NEXT_CONTROL_VALUE 1053 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/protocols/NewsAggregator/Src/stdafx.cxx b/protocols/NewsAggregator/Src/stdafx.cxx new file mode 100644 index 0000000000..1b563fc866 --- /dev/null +++ b/protocols/NewsAggregator/Src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h"
\ No newline at end of file diff --git a/protocols/NewsAggregator/Src/stdafx.h b/protocols/NewsAggregator/Src/stdafx.h new file mode 100644 index 0000000000..819e96d69f --- /dev/null +++ b/protocols/NewsAggregator/Src/stdafx.h @@ -0,0 +1,167 @@ +/* +Copyright (C) 2012 Mataes + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this file; see the file license.txt. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#pragma once + +// Windows Header Files: +#include <windows.h> +#include <commctrl.h> +#include <time.h> +#include <malloc.h> +#include <fcntl.h> +#include <io.h> +#include <sys/stat.h> +#include <mshtml.h> + +// Miranda header files +#include <newpluginapi.h> +#include <m_clist.h> +#include <m_langpack.h> +#include <m_options.h> +#include <m_protosvc.h> +#include <m_protoint.h> +#include <m_database.h> +#include <m_netlib.h> +#include <m_icolib.h> +#include <m_message.h> +#include <win2k.h> +#include <m_xml.h> +#include <m_avatars.h> +#include <m_hotkeys.h> +#include <m_gui.h> + +#include <m_folders.h> +#include <m_toptoolbar.h> + +#include "Options.h" +#include "version.h" +#include "resource.h" + +#define MODULENAME "NewsAggregator" +#define TAGSDEFAULT L"#<title>#\r\n#<link>#\r\n#<description>#" +#define DEFAULT_AVATARS_FOLDER "NewsAggregator" +#define DEFAULT_UPDATE_TIME 60 + +extern CDlgBase *pAddFeedDialog, *pImportDialog, *pExportDialog; +extern HNETLIBUSER hNetlibUser; +extern UINT_PTR timerId; +extern LIST<CFeedEditor> g_arFeeds; +// check if Feeds is currently updating +extern bool ThreadRunning; +extern bool UpdateListFlag; +extern wchar_t tszRoot[MAX_PATH]; + +struct CMPlugin : public PLUGIN<CMPlugin> +{ + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +//============ STRUCT USED TO MAKE AN UPDATE LIST ============ + +struct NEWSCONTACTLIST { + MCONTACT hContact; + struct NEWSCONTACTLIST *next; +}; + +typedef struct NEWSCONTACTLIST UPDATELIST; + +extern UPDATELIST *UpdateListHead; +extern UPDATELIST *UpdateListTail; + +void UpdateListAdd(MCONTACT hContact); +void UpdateThreadProc(void*); +void DestroyUpdateList(void); + +extern HANDLE hUpdateMutex; +extern HGENMENU hService2[7]; + +int NewsAggrInit(WPARAM wParam,LPARAM lParam); +int OptInit(WPARAM wParam, LPARAM lParam); +int NewsAggrPreShutdown(WPARAM wParam,LPARAM lParam); +void NetlibInit(); +void NetlibUnInit(); +void InitMenu(); +void InitIcons(); +HICON LoadIconEx(const char* name, bool big); +HANDLE GetIconHandle(const char* name); + +INT_PTR NewsAggrGetName(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetCaps(WPARAM wp, LPARAM lp); +INT_PTR NewsAggrSetStatus(WPARAM wp, LPARAM /*lp*/); +INT_PTR NewsAggrGetStatus(WPARAM/* wp*/, LPARAM/* lp*/); +INT_PTR NewsAggrLoadIcon(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetInfo(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrGetAvatarInfo(WPARAM wParam, LPARAM lParam); +INT_PTR NewsAggrRecvMessage(WPARAM wParam, LPARAM lParam); + +INT_PTR CheckAllFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR AddFeed(WPARAM wParam, LPARAM lParam); +INT_PTR ChangeFeed(WPARAM wParam, LPARAM lParam); +INT_PTR ImportFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR ExportFeeds(WPARAM wParam, LPARAM lParam); +INT_PTR CheckFeed(WPARAM wParam, LPARAM lParam); +INT_PTR EnableDisable(WPARAM wParam, LPARAM lParam); +int OnToolbarLoaded(WPARAM wParam, LPARAM lParam); +void CALLBACK timerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); +void CALLBACK timerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); + +bool IsMyContact(MCONTACT hContact); +void GetNewsData(wchar_t *szUrl, char **szData, MCONTACT hContact, CFeedEditor *pEditDlg); +time_t DateToUnixTime(const char *stamp, bool FeedType); +void CheckCurrentFeed(MCONTACT hContact); +void CheckCurrentFeedAvatar(MCONTACT hContact); +LPCTSTR CheckFeed(wchar_t* tszURL, CFeedEditor *pEditDlg); +void UpdateMenu(bool State); +LPCTSTR ClearText(CMStringW &value, const wchar_t *message); +bool DownloadFile(LPCTSTR tszURL, LPCTSTR tszLocal); +void CreateAuthString(char *auth, MCONTACT hContact, CFeedEditor *pDlg); +MCONTACT GetContactByNick(const wchar_t *nick); +MCONTACT GetContactByURL(const wchar_t *url); + +// =============== NewsAggregator SERVICES ================ +// Check all Feeds info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHECKALLFEEDS "NewsAggregator/CheckAllFeeds" + +// Add new Feed channel +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_ADDFEED "NewsAggregator/AddNewsFeed" + +// Change Feed channel +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHANGEFEED "NewsAggregator/ChangeNewsFeed" + +// Import Feed channels from file +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_IMPORTFEEDS "NewsAggregator/ImportFeeds" + +// Export Feed channels to file +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_EXPORTFEEDS "NewsAggregator/ExportFeeds" + +// Check Feed info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_CHECKFEED "NewsAggregator/CheckFeed" + +// Enable/disable getting feed info +// WPARAM = LPARAM = NULL +#define MS_NEWSAGGREGATOR_ENABLED "NewsAggregator/Enabled" diff --git a/protocols/NewsAggregator/Src/version.h b/protocols/NewsAggregator/Src/version.h new file mode 100644 index 0000000000..a7d2126e30 --- /dev/null +++ b/protocols/NewsAggregator/Src/version.h @@ -0,0 +1,13 @@ +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 1 +#define __RELEASE_NUM 0 +#define __BUILD_NUM 5 + +#include <stdver.h> + +#define __PLUGIN_NAME "News aggregator" +#define __FILENAME "NewsAggregator.dll" +#define __DESCRIPTION "RSS/Atom news aggregator." +#define __AUTHOR "Mataes, FREAK_THEMIGHTY" +#define __AUTHORWEB "https://miranda-ng.org/p/NewsAggregator/" +#define __COPYRIGHT "© 2012-19 Mataes, FREAK_THEMIGHTY" 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</title> + + <link rel="self" type="application/atom+xml;type=feed" href="http://code.google.com/feeds/p/watcher-miranda/updates/basic"/> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/updates/list"/> + + <entry> + <updated>2012-01-04T06:45:38Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12pftbjzm31hlgp204cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-0.10.latest-vc2010_x64-static.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2012-01-03T21:06:03Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12qdzfqdumlchjgm04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-v0100a2w11.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2012-01-03T20:47:25Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z135ypx45kehgtcq304cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda-v0100a2w11.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2012-01-03T09:21:55Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12szb1zzybuejnnv22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=mirotr1.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2012-01-02T20:12:28Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12ltxpzuvr4xvahk04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=miranda32.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2012-01-01T18:00:06Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13idtjadwu5y3q2322pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.1.0.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-30T11:06:47Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12vzfygpwqng3m3b22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda_Empoli_Pack_Mod.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-29T14:08:24Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z135in4x1yi1fd2ou04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=mirotr.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-25T10:59:24Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13dgrs5elfkfp0n122pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=LangpackSuite.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-25T10:58:18Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13cu14hyzzawjmev22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=LangpackSuite.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-21T21:55:28Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12sh1jqflzkj3ofd22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.9.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-13T19:46:02Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13nxvbpft3ezr5ps04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.8.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-04T10:52:49Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13vubiqswqqe1cf222pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Fingerprint.dll" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"></content> +</entry> + + + <entry> + <updated>2011-12-04T10:52:32Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z125fjy5qruixxloh04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=PackUpdater.dll" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"></content> +</entry> + + + <entry> + <updated>2011-12-03T15:01:42Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13etferquvuyzd0z22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=packupdater.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-12-02T22:21:54Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z12ysnhy4zi2wvudo22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=fingerprintmodplus.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-11-28T06:15:02Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13hy1xpcr2hgbrfv22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.7.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-11-24T16:35:48Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13mcvxqurmeybypn04cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.6.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-11-23T04:58:16Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13rxxbauu3wxtfmz22pfv4p3ma4ch5kj</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=simplestatusmsg.7z" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + + <entry> + <updated>2011-11-19T06:13:19Z</updated> + <id>http://code.google.com/feeds/p/watcher-miranda/updates/basic/z13di1rprvjfhpta004cepjgmoicsl1oq1w</id> + <link rel="alternate" type="text/html" href="http://code.google.com/p/watcher-miranda/downloads/detail?name=Miranda%20IM%20Watcher%20Pack%20v3.0.5.exe" /> + <title type="html"><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></title> + <author> + <name>watcherhd</name> + </author> + <category term="ProjectNewDownload" scheme="http://code.google.com/updates/type"/> + <content type="html"><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></content> +</entry> + + +</feed> 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 @@ +<?xml version="1.0" encoding="windows-1251"?> +<rss version="2.0" + xmlns:atom="http://www.w3.org/2005/Atom" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:content="http://purl.org/rss/1.0/modules/content/"> + <channel> + <title>Bash.Org.Ru</title> + <link>http://bash.org.ru/</link> + <atom:link href="http://bash.org.ru/rss/" rel="self" type="application/rss+xml" /> + <description>Цитатник Рунета</description> + <language>ru</language> + <item> + <guid isPermaLink="false">b3cf2d64d57bdfea405b43426ec354e4b89a98912c71f243c93568020e2f0a70</guid> + <link>http://bash.org.ru/quote/414990</link> + <title>Цитата #414990</title> + <pubDate>Fri, 30 Dec 2011 12:12:01 +0400</pubDate> + <description><![CDATA[xxx: Это все равно что брызгать на порнобаннер святой водой, а потом, когда монитор закоротит, кричать, мол, дьявольское устройство не вынесло присутствия святого духа<br>]]></description> + </item> + <item> + <guid isPermaLink="false">9f3028db6c6020de238a5d27b14d85c21e5f4c8a5f96506b757925df8500c940</guid> + <link>http://bash.org.ru/quote/414989</link> + <title>Цитата #414989</title> + <pubDate>Fri, 30 Dec 2011 11:45:18 +0400</pubDate> + <description><![CDATA[В полной мере осознал, что перешел на ночной образ жизни, когда отец разбудил в час ночи с вопросами: "Ты чего спишь? Не заболел случаем?"]]></description> + </item> + <item> + <guid isPermaLink="false">c917324b76e57d2d6103af4b154f41fb335e9a4cc3f973b0b077a8c857c1353a</guid> + <link>http://bash.org.ru/quote/414988</link> + <title>Цитата #414988</title> + <pubDate>Fri, 30 Dec 2011 11:44:38 +0400</pubDate> + <description><![CDATA[ххх: ну как тебе фильм?<br>ууу: конец тру<br>ххх: ты фильм не перепутал?<br>ууу: черт, в смысле, финал меня приятно удивил)]]></description> + </item> + <item> + <guid isPermaLink="false">96b70712a7185b7d1167f6f9aee454841a4246b0f270185ea3f29d034b9bd39f</guid> + <link>http://bash.org.ru/quote/414987</link> + <title>Цитата #414987</title> + <pubDate>Fri, 30 Dec 2011 11:44:01 +0400</pubDate> + <description><![CDATA[Отзыв о телефоне с сенсорным экраном:<br>Света:<br>Купила его в ***. Классный телефон!!! Ещё не разобралась, как снять блокировку с экрана. Он красивый.]]></description> + </item> + <item> + <guid isPermaLink="false">793886d798688d3e6bec7d2ffb51d6397f356e5c0e00ef06d07f22b87643c93a</guid> + <link>http://bash.org.ru/quote/414986</link> + <title>Цитата #414986</title> + <pubDate>Fri, 30 Dec 2011 11:13:16 +0400</pubDate> + <description><![CDATA[xxx: сегодня пришла посылка посылка с ebay в которой 50шт наклеек от ipad. Теперь у меня iДом, а в нем iУнитаз, iШкаф, iХолодильник и даже iКот куда то убежал....]]></description> + </item> + <item> + <guid isPermaLink="false">6eac3f54c4f58d8a41c7e7f72f72ca6e0c6bf5869f0eefc99044ef4427db28d0</guid> + <link>http://bash.org.ru/quote/414985</link> + <title>Цитата #414985</title> + <pubDate>Fri, 30 Dec 2011 11:12:02 +0400</pubDate> + <description><![CDATA[xxx: отдам 4-х котят в любые руки, им уже 3 месяца и они всех задолбали.]]></description> + </item> + <item> + <guid isPermaLink="false">744d1d505dce7f1877c45baee1fdf97009a403d6dedb3ae1cd185480ec3dbbd0</guid> + <link>http://bash.org.ru/quote/414984</link> + <title>Цитата #414984</title> + <pubDate>Fri, 30 Dec 2011 11:11:01 +0400</pubDate> + <description><![CDATA[xxx:<br>Где купить кормовых мышей (лысых)?<br>На зоорынке нету.<br><br>yyy:<br>Довели страну... Народ уже мышей жрать начал.]]></description> + </item> + <item> + <guid isPermaLink="false">aadc7f6194e8062c44250a9a878f010c0fe7d9d95af9a8389fbf8a8f748d6c6f</guid> + <link>http://bash.org.ru/quote/414983</link> + <title>Цитата #414983</title> + <pubDate>Fri, 30 Dec 2011 10:45:08 +0400</pubDate> + <description><![CDATA[xxx: у нас ёлка не простая, понтовая<br>xxx: с оптоволокном и светится<br>yyy: wifi бесплатно не раздает?]]></description> + </item> + <item> + <guid isPermaLink="false">01511c230d7cdf4ed42f09106b3075a5021797ae5a11d3063dc9332bb7baf858</guid> + <link>http://bash.org.ru/quote/414982</link> + <title>Цитата #414982</title> + <pubDate>Fri, 30 Dec 2011 10:44:36 +0400</pubDate> + <description><![CDATA[Darkforce: А ты к экзамену с музыкой готовишься?<br>Ergo: Да. И с включенной асей. Не бери с меня пример.]]></description> + </item> + <item> + <guid isPermaLink="false">cc05613e48d3f285e2a2a95a12f542586c5c0fe4499addf3c2a633cf4940e572</guid> + <link>http://bash.org.ru/quote/414981</link> + <title>Цитата #414981</title> + <pubDate>Fri, 30 Dec 2011 10:44:01 +0400</pubDate> + <description><![CDATA[<Jakiro> сегодня вешал гирлянду, запутался в ней, сел, пришла сестра, включила гирлянду и пошла ржать :(]]></description> + </item> + <item> + <guid isPermaLink="false">589fe99078c1447d672503cd8ffb9ba7524d9bd9e03cb5444e7c6fcd2be55269</guid> + <link>http://bash.org.ru/quote/414980</link> + <title>Цитата #414980</title> + <pubDate>Fri, 30 Dec 2011 10:12:44 +0400</pubDate> + <description><![CDATA[SeeR: Если считать коэффициент лени равным отношению того, что нужно было сделать, к тому, что сделано, то по воскресеньям я традиционно делю на ноль.]]></description> + </item> + <item> + <guid isPermaLink="false">58b960aeadb0e2700dd3ec5c18a0f9ca1f446d49f66edabf2ad3ea0f4a9f4933</guid> + <link>http://bash.org.ru/quote/414979</link> + <title>Цитата #414979</title> + <pubDate>Fri, 30 Dec 2011 10:12:01 +0400</pubDate> + <description><![CDATA[Стина: Глупо было принимать ванну с бомбочкой с блестками. Мы в блестках, полотенца в блестках, постельное белье в блестках, даже кошка уже в блестках.<br>Но еще глупее было наутро идти к гинекологу! >.<]]></description> + </item> + <item> + <guid isPermaLink="false">dda44cf9be0c8bf4691ce24629fd67dca371952e1dc45aa5dc1cce16ca095d29</guid> + <link>http://bash.org.ru/quote/414978</link> + <title>Цитата #414978</title> + <pubDate>Fri, 30 Dec 2011 09:46:04 +0400</pubDate> + <description><![CDATA[BaDMaN: ненавижу утро субботы. Правда, оно не всегда утром и не всегда в субботу.]]></description> + </item> + <item> + <guid isPermaLink="false">33f18d83baf27fbd1e28e525c942b2c7e379b42a7d91f7436e2e199400ceda30</guid> + <link>http://bash.org.ru/quote/414977</link> + <title>Цитата #414977</title> + <pubDate>Fri, 30 Dec 2011 09:45:01 +0400</pubDate> + <description><![CDATA[ХХХ: Скайримские норды такие брутальные, что суровые челябинские мужики по сравнению с ними девочки из песочницы. Но при этом в каждой пещере самого жестокого разбойника, в каждом доме самого яростного норда на столе или полке стоит сладкий рулет.<br>УУУ: Рулетики фигня. Все тамриэльские разбойники и мародёры поддерживают среди себя высокую грамотность и даже в самом распропащем, заросшем мхом и паутиной логове всегда есть две-три книжечки. Вот так приходит брутальный скайримский дорожный разбойник после тяжёлого трудового будня, садится на хлипкий табуретик, берёт сладкий рулетик и читает "краткую историю империи". брутальненько.]]></description> + </item> + <item> + <guid isPermaLink="false">5599090163fe32efddbf295e457fd9cc4f3426b524eebaaaa4fa8277f34e9be2</guid> + <link>http://bash.org.ru/quote/414976</link> + <title>Цитата #414976</title> + <pubDate>Fri, 30 Dec 2011 09:13:01 +0400</pubDate> + <description><![CDATA[xxx: мой друг каждую субботу ходит в кинотеатр!<br>yyy: торренты замаливает?]]></description> + </item> + <item> + <guid isPermaLink="false">1bb26a5bc553b0570cbdf64be1a9ba3af74c3fd64dc60fe0bf9335ecb74600d0</guid> + <link>http://bash.org.ru/quote/414975</link> + <title>Цитата #414975</title> + <pubDate>Fri, 30 Dec 2011 08:45:35 +0400</pubDate> + <description><![CDATA[xxx: Маленький Петя очень любит подарки. Его мама подарила ему на день рождения две строки равной длины, состоящие из больших и маленьких букв латинского алфавита. Теперь Петя хочет сравнить эти строки лексикографически. Помогите Пете выполнить сравнение. (facepalm)<br>yyy: Помогите Пете не закончить жизнь суицидом :D]]></description> + </item> + <item> + <guid isPermaLink="false">cf4da48b55d3fdb9e9825e39a664bd4d41dc5c7a64227796ad1afb0900bdec19</guid> + <link>http://bash.org.ru/quote/414974</link> + <title>Цитата #414974</title> + <pubDate>Fri, 30 Dec 2011 08:45:01 +0400</pubDate> + <description><![CDATA[Давным-давно, когда ещё не было батареек и аккумуляторов, садилось солнце. Его тоже хватало от силы на день, не больше.<br>]]></description> + </item> + <item> + <guid isPermaLink="false">1a7a8ba066100acdf40b79f4fbc967932320641e3565b230678920432e80e1e3</guid> + <link>http://bash.org.ru/quote/414973</link> + <title>Цитата #414973</title> + <pubDate>Fri, 30 Dec 2011 08:12:44 +0400</pubDate> + <description><![CDATA[xxx: только в армии понимаешь, что зубная щетка реально для труднодоступных мест...]]></description> + </item> + <item> + <guid isPermaLink="false">339e00558b293e087749ba42e0f06916531e712b1edfb25e470e2c0d10494aea</guid> + <link>http://bash.org.ru/quote/414972</link> + <title>Цитата #414972</title> + <pubDate>Fri, 30 Dec 2011 08:11:44 +0400</pubDate> + <description><![CDATA[С форума<br><br>XXX: Здравствуйте, скажите пожалуйста, каков срок годности у сигарет?<br>Купил пачку сигарет и чувствую, что какие то они хреновые, сначала подумал, что это связано с тем, что я сейчас пытаюсь бросить курить (раньше курил пачку в день, а щас пачку на неделю тяну) и начал отвыкать, потом глянул на срок изготовления, а там написано июль 2008г...<br><br>YYY: Боитесь навредить здоровью? )))]]></description> + </item> + <item> + <guid isPermaLink="false">cfa6ff32cedafbf49ef812730e850a6abc9ec61c7682f3518d95fa00db4e1a14</guid> + <link>http://bash.org.ru/quote/414971</link> + <title>Цитата #414971</title> + <pubDate>Fri, 30 Dec 2011 08:11:01 +0400</pubDate> + <description><![CDATA[R*: ничо так прога. рабочие места пользователей на схеме можно изобразить очень разнообразно - есть картинки оленя, кенгуру, овцы, жирафа]]></description> + </item> + <item> + <guid isPermaLink="false">128696d56057a228c43deb8cad49205905868817d0b4e834e37e3fdaccecafb4</guid> + <link>http://bash.org.ru/quote/414970</link> + <title>Цитата #414970</title> + <pubDate>Thu, 29 Dec 2011 12:12:42 +0400</pubDate> + <description><![CDATA[xxx: Я такая, какая я есть!!<br>yyy: Да, и глаза у тебя цвета твоих глаз.]]></description> + </item> + <item> + <guid isPermaLink="false">2d15491a51b88af3e66c02b4d326f1e2802656b6361f8f07dd667fbbe9dce728</guid> + <link>http://bash.org.ru/quote/414969</link> + <title>Цитата #414969</title> + <pubDate>Thu, 29 Dec 2011 12:11:34 +0400</pubDate> + <description><![CDATA[ххх: Да у меня вообще такое ощущение, что все покупаемые мною средства для укрепления и роста волос не задерживаясь на голове начинают действовать в области нижних конечностей и промежности...]]></description> + </item> + <item> + <guid isPermaLink="false">ed64e480321c46a51035c44a445cfd98927e2e303d972ac8874e8e36c11a8371</guid> + <link>http://bash.org.ru/quote/414968</link> + <title>Цитата #414968</title> + <pubDate>Thu, 29 Dec 2011 12:11:01 +0400</pubDate> + <description><![CDATA["Почему программисты работают по ночам" @habr<br><br>troydm: Абсолютно согласен с автором, именно поэтому я ложусь спать в 5 утра и просыпаюсь в час дня! Всем спокойной ночи!<br>muzhig: Это можно себе позволить, если нет жены. А вот моей очень не нравится когда я работаю по ночам. Все объяснения про то что «ночью намного лучше работается» она не хочет воспринимать всерьёз.<br>ozgg: Ну жену можно тоже научить программировать, и график синхронизируется ;)<br>Vexilurz: Конечно. Делов-то на 5 минут]]></description> + </item> + <item> + <guid isPermaLink="false">aaa2f3a8dd486d209a697ecd4e3d728d99a2aa37dd482a3a859af5c473143b69</guid> + <link>http://bash.org.ru/quote/414967</link> + <title>Цитата #414967</title> + <pubDate>Thu, 29 Dec 2011 11:46:07 +0400</pubDate> + <description><![CDATA[Нашел на сайте одной научной лаборатории в сотрудниках человека, который лет эдак 16 назад угнал у меня велик. Вот думаю, может сходить, попросить у него велосипед обратно, он же говорил "да я прокачусь вон до туда и верну!" :)))]]></description> + </item> + <item> + <guid isPermaLink="false">dc470e391715c450eef79e82aaadfb0342ecc256504946f0a32d2f8c1160557e</guid> + <link>http://bash.org.ru/quote/414966</link> + <title>Цитата #414966</title> + <pubDate>Thu, 29 Dec 2011 11:45:06 +0400</pubDate> + <description><![CDATA[vova_belkin:<br><br>Вот мой прадедушка, например, ухитрился прокутить и проиграть в карты до копейки все имение, наследство и женино приданое аккурат к 1917 году.<br><br>И с тех пор считался в нашем семействе непререкаемым авторитетом в вопросах распоряжения капиталом.]]></description> + </item> + <item> + <guid isPermaLink="false">a7147227d4c9f64edae338c2cec9f4456a1214ea3f9d899c2486038abe99a8f5</guid> + <link>http://bash.org.ru/quote/414965</link> + <title>Цитата #414965</title> + <pubDate>Thu, 29 Dec 2011 11:44:01 +0400</pubDate> + <description><![CDATA[ну-ну: если атеизм - это религия, то не собирать марки - это хобби]]></description> + </item> + <item> + <guid isPermaLink="false">d35adf64ed961a7e7ff88e11a9b57760b1cd3f0a835f008b6e9782a27ee1a31a</guid> + <link>http://bash.org.ru/quote/414964</link> + <title>Цитата #414964</title> + <pubDate>Thu, 29 Dec 2011 11:13:11 +0400</pubDate> + <description><![CDATA[xxx: да ну нахер эту сессию. Пока учил дискретную математику, забыл пароли от двух почтовых ящиков.]]></description> + </item> + <item> + <guid isPermaLink="false">8ce06ad45b760d6b51e2432476e5c13dc9158f589c9b662147b760b0a1b967ac</guid> + <link>http://bash.org.ru/quote/414963</link> + <title>Цитата #414963</title> + <pubDate>Thu, 29 Dec 2011 11:12:01 +0400</pubDate> + <description><![CDATA[Обсуждение новой антресоли в кладовке:<br><br>Ну там все как надо сделали. Но барахла традиционно больше, чем места под него. Причем вот в чем загадка бытия: вынимаешь барахло из кладовки, половину выкидываешь, оставшееся убираешь в кладовку, а оно туда не влезает!]]></description> + </item> + <item> + <guid isPermaLink="false">c64eb292881cc760ac0d2f7ac0b1010a8047b1271d39a0bc759076633640bbb4</guid> + <link>http://bash.org.ru/quote/414962</link> + <title>Цитата #414962</title> + <pubDate>Thu, 29 Dec 2011 10:45:24 +0400</pubDate> + <description><![CDATA[Lilith: я боюсь готовить кальмара<br>Lightning: почему???<br>Lilith: у него клюв, и еще надо что-то сдирать как чулок<br>Lightning: че ты боишься, там все сходит классно<br>Lilith: вдруг я сдеру кальмара, а съем чулок<br>Lilith: а еще у них там чернильница вроде, и икра<br>Lightning: ну блин, ты потрошила кого-нибудь вообще когда-нибудь?<br>Lilith: я что, похожа на самца кальмара?<br>Lilith: только копченую мойву<br>Lightning: а курицу? не? ее так смешно еще можно надевать на руку и играть в театр<br>Lightning: шевелить крылышками, если через жопу вставить<br>Lightning: так вот, кальмара легче потрошить, он мягонький такой<br>Lightning: и жопы нет]]></description> + </item> + <item> + <guid isPermaLink="false">19ee3858da403abdebc2f2694ade27e224d83cb2d0325964d802f3313c1333de</guid> + <link>http://bash.org.ru/quote/414961</link> + <title>Цитата #414961</title> + <pubDate>Thu, 29 Dec 2011 10:44:36 +0400</pubDate> + <description><![CDATA[муж: да как, как, херово :( не переживу я, наверное, испытательный срок<br>я: почему??<br>муж: да вот блин. решил показать себя в хорошем свете перед шефом. пожелал ему приятного аппетита. а он шел не в столовую а в туалет.]]></description> + </item> + <item> + <guid isPermaLink="false">e0270fd309b6e90cb9f26d9012a1db8ed681fd071f5848b395535442033ffe3a</guid> + <link>http://bash.org.ru/quote/414960</link> + <title>Цитата #414960</title> + <pubDate>Thu, 29 Dec 2011 10:44:01 +0400</pubDate> + <description><![CDATA[Luba: Чем занимаешься в отпуске?<br>BreakDancer: Разными важными делами<br>Luba: Например?<br>BreakDancer: Да вот вспомнил, что в детстве у меня был пистолет, стреляющий присосками<br>BreakDancer: и решил, что раз уж я вырос до 29 лет и стал большим<br>BreakDancer: то и присоски тоже должны быть большими!<br>BreakDancer: Сделал арбалет, который стреляет вантусом]]></description> + </item> + <item> + <guid isPermaLink="false">8187c704ade265c329ccc02384e4206607407657bc6c9b9425a9542f0cb5a6a1</guid> + <link>http://bash.org.ru/quote/414959</link> + <title>Цитата #414959</title> + <pubDate>Thu, 29 Dec 2011 10:13:01 +0400</pubDate> + <description><![CDATA[Обещают, что дети, рожденные в год Черного Дракона, будут творческими и активными. Но мы-то знаем, что главное для них - иммунитет к магии!]]></description> + </item> + <item> + <guid isPermaLink="false">1e5eadf8678f61893bcf84e0dfb41290f63266eab736941b7ca07653b70f17d4</guid> + <link>http://bash.org.ru/quote/414958</link> + <title>Цитата #414958</title> + <pubDate>Thu, 29 Dec 2011 09:45:26 +0400</pubDate> + <description><![CDATA[Звонит курьер из одной конторы и спрашивает как добраться.<br>Я: далее мост через реку, идете по мосту...<br>К: А река где в это время? Слева? Справа?<br>Я: ВНИЗУ!<br>К: ага, понятно...<br>Я: Дальше переходите на другой берег, спускаетесь на набережную...<br>К: А чё, прямо до конца моста идти? или раньше повернуть?]]></description> + </item> + <item> + <guid isPermaLink="false">b7c708279640f1efe4ec3401970cb89f075864203ed0fbeeb612e9592655a4c3</guid> + <link>http://bash.org.ru/quote/414957</link> + <title>Цитата #414957</title> + <pubDate>Thu, 29 Dec 2011 09:44:52 +0400</pubDate> + <description><![CDATA[gutta_honey@lj<br>Лиза фанат "Звездных войн". У нее есть фигурка Дарта Вейдера. Но девчонка есть девчонка. Она, как с пупсиком, с ним играет. Вот сегодня:<br>- Дарту Вейдеру нужна квартира! Я его поселю в коробке из-под апельсинов.<br>Вот так в женских руках даже злодеев галактического масштаба постигает судьба чебурашки.]]></description> + </item> + <item> + <guid isPermaLink="false">e91f03e5e391060d916a7e7e827cb6eaeec132c17a1ac5d78af70d5368461ec5</guid> + <link>http://bash.org.ru/quote/414956</link> + <title>Цитата #414956</title> + <pubDate>Thu, 29 Dec 2011 09:44:01 +0400</pubDate> + <description><![CDATA[ХХХ: кто рано встаёт, того кот разбудил :(]]></description> + </item> + <item> + <guid isPermaLink="false">c805cfaf28bf072384933e50ea8e1c48f815063c3bfbfe05a0986d6b944bd14e</guid> + <link>http://bash.org.ru/quote/414955</link> + <title>Цитата #414955</title> + <pubDate>Thu, 29 Dec 2011 09:13:01 +0400</pubDate> + <description><![CDATA[XXX: Бежать, бежать отсюда!<br>YYY: Что у тебя там?<br>XXX: Только что коммерческий с исполнительным в кабинете орали друг на друга: "Да я таких как ты в Афгане к стенке ставил" vs "А я таких как ты на зоне петушил". Мне страшно.]]></description> + </item> + <item> + <guid isPermaLink="false">0636538a5462fc292f9b4fc6439cd6f7f8ab0fe79ba081c47e36d3d8338806fe</guid> + <link>http://bash.org.ru/quote/414954</link> + <title>Цитата #414954</title> + <pubDate>Thu, 29 Dec 2011 08:46:01 +0400</pubDate> + <description><![CDATA[xxx: Да ты что, это же баянъ.<br>xxx: Круговорот интернета. В твиттер постят записи пятилетней давности из ЖЖ.<br>yyy: вот не видела, жж обошел меня стороной<br>ххх: Так я и говорю, в ЖЖ постили шутки с форумов, в форумы - с фидо...<br>ххх: и только в фидо основоположники сетевого юмора перепечатывали анекдоты из газет]]></description> + </item> + <item> + <guid isPermaLink="false">c8591055f839c5dbc882d36e7a4d2db9ce399460048e8e0e6d466dd43a88cae1</guid> + <link>http://bash.org.ru/quote/414953</link> + <title>Цитата #414953</title> + <pubDate>Thu, 29 Dec 2011 08:13:04 +0400</pubDate> + <description><![CDATA[О сексе:<br>xxx: отлично! в лабораторных такого не было,но я ЗА! )))<br>yyy: В лабораториях опасно. зацепил не ту колбу - и все, чем гордился всю жизнь и с чем жил - с тихим шипением капает на пол... =(<br>xxx: Или высоковольтная установка по крестцовому сплетению... Струя эякулята пробивает кирпичную стену и ранит лектора в соседней аудитории!<br>xxx: Кажется, я только что придумал классное аниме, да?]]></description> + </item> + <item> + <guid isPermaLink="false">714be978a8678d201b16981150150166752145feaefa2f159980d76051602d87</guid> + <link>http://bash.org.ru/quote/414952</link> + <title>Цитата #414952</title> + <pubDate>Thu, 29 Dec 2011 08:11:54 +0400</pubDate> + <description><![CDATA[Реклама на Яндексе известной компании и слоган "Попробуй с нашим выбиратором подарков!"<br>Ага, я тоже первый раз неправильно прочитал =)]]></description> + </item> + <item> + <guid isPermaLink="false">dd7d36ba1543a7ac360b28c72f669816d3551bb09f34156635f80efbc9a4f390</guid> + <link>http://bash.org.ru/quote/414951</link> + <title>Цитата #414951</title> + <pubDate>Thu, 29 Dec 2011 08:11:01 +0400</pubDate> + <description><![CDATA[Сегодня выносил мусор в офисе, а моя коллега играла в винду, задавая вопросы:<br>- Вы уверены, что хотите очистить корзину? Файлы будут удалены безвозвратно.<br>А потом у меня порвался пакет и весь мусор разлетелся по офису, на что коллега печально заметила:<br>- Произошел сбой при очистке корзины, перезагрузитесь и попробуйте снова...]]></description> + </item> + <item> + <guid isPermaLink="false">174e14053635b5325be15ae0a37b47953115b33e0d082053064572907d2829a0</guid> + <link>http://bash.org.ru/quote/414950</link> + <title>Цитата #414950</title> + <pubDate>Wed, 28 Dec 2011 12:46:01 +0400</pubDate> + <description><![CDATA[[QuizMaster7] Повторяю вопрос: Именно они вешали под шею коня волчью голову, а на седло прикрепряли метлу, чтобы вынюхивать измену и выметать ее из государства<br>[QuizMaster7] Первая подсказка: Количество букв: 9<br>[EwokDoUrden] фигасе!<br>[EwokDoUrden] НАРКОМАНЫ]]></description> + </item> + <item> + <guid isPermaLink="false">691660b05378e2d242a7a60429e2cd94a44e8d2041b2facb2597cb21ec213fbf</guid> + <link>http://bash.org.ru/quote/414949</link> + <title>Цитата #414949</title> + <pubDate>Wed, 28 Dec 2011 12:13:18 +0400</pubDate> + <description><![CDATA[Док: Бля, ходил покурить на балкон, вижу в углу что-то мохнатое с угрожающей тенью. В сторону шарахнулся. И уже думаю, глюки от бессонницы прут. Не блин, я просто забыл, что вчера елку купил.]]></description> + </item> + <item> + <guid isPermaLink="false">634c63c05a37bbb5933ccae9bd50bbc4e04d2a27dfd2895ab8f4be5cc77a5707</guid> + <link>http://bash.org.ru/quote/414948</link> + <title>Цитата #414948</title> + <pubDate>Wed, 28 Dec 2011 12:12:02 +0400</pubDate> + <description><![CDATA[XXX: все, финиш! я конечно разных дебилов видела. и к скрепкам в принтере уже даже почти привыкла. и скотч на барабане, блин, приклеенный с честными глазами "ну я же его даже не открывала, я не знаю, как он туда попал!". но чтобы так! у меня сейчас принтер КАЛЬКУЛЯТОР зажевал!!! калькулятор, блин!]]></description> + </item> + <item> + <guid isPermaLink="false">2309307a0dd79a2f77f63dbbbf3239d5de3cd41409ce23750c0e8ebecd20466f</guid> + <link>http://bash.org.ru/quote/414946</link> + <title>Цитата #414946</title> + <pubDate>Wed, 28 Dec 2011 11:44:57 +0400</pubDate> + <description><![CDATA[xxx:<br>отдам в дар плеер<br>Explay T-7, слегка подержаный (вхламину)]]></description> + </item> + <item> + <guid isPermaLink="false">c58a33a67db26dab4b121d14ebad5cd80d78040eea517835a90bbdc81887b4dc</guid> + <link>http://bash.org.ru/quote/414945</link> + <title>Цитата #414945</title> + <pubDate>Wed, 28 Dec 2011 11:44:01 +0400</pubDate> + <description><![CDATA[Купили с сотрудницей в офис ликера. Половину выпили, остатки она заматывает в пакетик и ставит в холодильник.<br>Я: Мы что, тут его и оставим?<br>Сотрудница: Ну да, а что нам терять? )<br>Я: Ну, например работу....]]></description> + </item> + <item> + <guid isPermaLink="false">3a8ab041c452b16f9f415d3f6d209f1d58f11ed2ab0ee5f928a5f6b8e22a78b9</guid> + <link>http://bash.org.ru/quote/414944</link> + <title>Цитата #414944</title> + <pubDate>Wed, 28 Dec 2011 11:12:55 +0400</pubDate> + <description><![CDATA[<Belka> Весьма смутилась, найдя в записной телефонной книжке своего безграмотного брата запись "Макс -сосет".]]></description> + </item> + <item> + <guid isPermaLink="false">d72696e62bb007bb3004d7403e5915734f7ca02ef54757e08cb0a689a1b4df4d</guid> + <link>http://bash.org.ru/quote/414943</link> + <title>Цитата #414943</title> + <pubDate>Wed, 28 Dec 2011 11:12:01 +0400</pubDate> + <description><![CDATA[korsa: вокруг меня три компа, два телефона и навигатор<br>fintar: нужно перевезти их через реку?]]></description> + </item> + <item> + <guid isPermaLink="false">9ffbfa3d0268a0dc955b9969e4c2b77cc50443a7d6bd9b60f9cf4102daeb7fb1</guid> + <link>http://bash.org.ru/quote/414942</link> + <title>Цитата #414942</title> + <pubDate>Wed, 28 Dec 2011 10:45:41 +0400</pubDate> + <description><![CDATA[Из обсуждения:<br>Как–то раз я в споре со своей женой обнаружил какую–то откровенную дурь в ее словах и сказал:<br>— Но это же не логично!<br>— Ну и что?<br>Блядь, НУ И ЧТО? в этот момент у меня вообще весь мир рухнул, и я понял что для нее вообще нет разницы, логично или нет то, что она говорит. ВООБЩЕ!<br><br>Когда я пересказываю это своим друзьям, все хохочут или улыбаются. А когда девушкам — они просто ждут, что я дальше скажу, со внимательным лицом, потому что искренне не замечают проблемы в этой фразе]]></description> + </item> + <item> + <guid isPermaLink="false">6017310e922bd529b5ad3be09f4f0c5e8b6b2fb76d0bd05b617a8eac0ae27193</guid> + <link>http://bash.org.ru/quote/414941</link> + <title>Цитата #414941</title> + <pubDate>Wed, 28 Dec 2011 10:44:47 +0400</pubDate> + <description><![CDATA[xxx: у нас в столовой давно вместо салфеток бумага промышленная нарезана, но сейчас она еще и глянцевая]]></description> + </item> + <item> + <guid isPermaLink="false">b101af3976aa2e9b7841576433b0c68af93c3ce85ac75383a6f7b20a0106a9c6</guid> + <link>http://bash.org.ru/quote/414940</link> + <title>Цитата #414940</title> + <pubDate>Wed, 28 Dec 2011 10:44:01 +0400</pubDate> + <description><![CDATA[Сейчас с женой по телефону разговаривал.<br>Она говорит:<br>- Давай тебе на НГ рубашку пошьем клевую и это будет моим подарком.<br>- Как хочешь.<br>Предлагает мерки поехать снять, я начинаю диктовать.<br>- Длина 174 см, ширина 80 см. Внутри ткань бархат, снаружи дерево, лучше дуб, крышка тоже дубовая резная.<br>Она говорит:<br>- Все записала, сейчас им позвоню.<br>И тут она прочитала то, что записала...]]></description> + </item> + <item> + <guid isPermaLink="false">1d3da2d78ecf5bac86c480e26d65184e8b69cab96bc8d6c038de7de9ca468ebd</guid> + <link>http://bash.org.ru/quote/414939</link> + <title>Цитата #414939</title> + <pubDate>Wed, 28 Dec 2011 10:13:07 +0400</pubDate> + <description><![CDATA[ххх: У нас сегодня экзамен по анатомии вообще зачетный вышел. Один парень срез гортани назвал срезом шейки матки, другой в ромбовидной ямке мозга приметил, звиняюсь, вульву.<br>ххх: Обоих отправили на пересдачу с формулировкой "вылечите недотрах и приходите снова".]]></description> + </item> + <item> + <guid isPermaLink="false">0365ddac152e7a1f45d4fe35cc5d673ff9e9cbce8d9d125e40b5dca139cd293e</guid> + <link>http://bash.org.ru/quote/414938</link> + <title>Цитата #414938</title> + <pubDate>Wed, 28 Dec 2011 10:12:01 +0400</pubDate> + <description><![CDATA[xxx: Жена после суток на работе попросила разбудить ее в 20:00. Звоню, она поднимает трубку, ничего не говорит, слышно только мирное сопение... Выключила будильник, блин :D]]></description> + </item> + <item> + <guid isPermaLink="false">8774184a10feb87ab76c3eac63fcf6873b572cd376d06b87ec2cafa48464b57b</guid> + <link>http://bash.org.ru/quote/414937</link> + <title>Цитата #414937</title> + <pubDate>Wed, 28 Dec 2011 09:45:31 +0400</pubDate> + <description><![CDATA[Ascha: как удобно: если реветь два часа подряд - то тушь смывать уже не нужно!]]></description> + </item> + <item> + <guid isPermaLink="false">5f9e58d114c8a86764bd26780e94bb3f00f0a4db026821f84d06b4540db7b5ed</guid> + <link>http://bash.org.ru/quote/414936</link> + <title>Цитата #414936</title> + <pubDate>Wed, 28 Dec 2011 09:45:01 +0400</pubDate> + <description><![CDATA[xxx: я зайду щас?<br>yyy: Я - горячая штучка. И я уже вся теку. Но будь со мной осторожен.<br>xxx: Аня?<br>yyy: У меня температура. Сопли ручьем. Рискуешь заразиться.]]></description> + </item> + <item> + <guid isPermaLink="false">8b1acd9ec8457ac318c78b1dc88005edf4fc9f0ace4d47c1080c1b670de19e2e</guid> + <link>http://bash.org.ru/quote/414935</link> + <title>Цитата #414935</title> + <pubDate>Wed, 28 Dec 2011 09:13:01 +0400</pubDate> + <description><![CDATA[Duck: Решил поправить здоровье, записался в бассейн.<br>Dimes: уринотерапия не работает]]></description> + </item> + <item> + <guid isPermaLink="false">187c05af1edfcce0de5da82d30a58fb83ae474e6fa897ad684bd3ad7c50e9dd7</guid> + <link>http://bash.org.ru/quote/414934</link> + <title>Цитата #414934</title> + <pubDate>Wed, 28 Dec 2011 08:46:02 +0400</pubDate> + <description><![CDATA[xxx: Меня попросили в метро пройти через рамку<br>yyy: какого х?<br>xxx: Хрен его знает<br>yyy: ты показался им подозрительным? )<br>xxx: Был как всегда в джинсах чистых ботинках кожаной сумке и наглой рожей<br>xxx: Меня как правило они вообще не замечают<br>yyy: хы<br>yyy: ты был в кожаной сумке, это настораживает]]></description> + </item> + <item> + <guid isPermaLink="false">00822e572762f43d2fb56ef00c2f7be12f703a27a39949f99036f7734a26f566</guid> + <link>http://bash.org.ru/quote/414933</link> + <title>Цитата #414933</title> + <pubDate>Wed, 28 Dec 2011 08:45:02 +0400</pubDate> + <description><![CDATA[misterIT: Сань, привет. срочно нужна помощь, через сколько можешь в офис подгрести?<br>misterIT: Домен рухнул, рейд заглючило. И винт прихвати.<br>misterIT: Сань!!! ты тут?<br>xxx@qip: Тут, собираюсь, а что случилось то? Напряжение? Коротнуло?<br>misterIT: Да хз<br>misterIT: Бороду вчера сбрил, свидание блядь видите ли!<br>xxx@qip: А я свитер постирал. Видимо не врали.]]></description> + </item> + <item> + <guid isPermaLink="false">a6231be02247fcfffbaa07e020f43a4c7a1c7ed628ed86af16443ffe45ed7127</guid> + <link>http://bash.org.ru/quote/414932</link> + <title>Цитата #414932</title> + <pubDate>Wed, 28 Dec 2011 08:12:38 +0400</pubDate> + <description><![CDATA[xxx: Женщины - как компьютерные игры с разными уровнями сложности. Если играть на Very Easy - то быстро пропадает интерес к игре. А если играть на Very Hard - то можно натереть мозоли джойстиком.]]></description> + </item> + <item> + <guid isPermaLink="false">039ba4efda1619b3b4374b223e344b3423a8d9788a18bf73ac07bce7bac04bea</guid> + <link>http://bash.org.ru/quote/414931</link> + <title>Цитата #414931</title> + <pubDate>Wed, 28 Dec 2011 08:12:01 +0400</pubDate> + <description><![CDATA[Я:<br>Жена мне такая говорит:<br>- мне нужна шуба норковая,<br>- у тебя же есть,<br>- у меня из кусочков, а мне надо цельную, у нас у всей группы уже цельные, а z одна как лошара!<br>- у всей - это у одной Людки?<br>- ну нет, у двоих точно есть!<br>- ... а мне нужен фотоаппарат кэнон 5д марк 2<br>- у тебя же есть уже.<br>- ну так у меня 5д, а я хочу чтобы был марк 2, а то у всех уже в городе марки, а я один как лошара<br>- ну тебе-то просто хочется, а мне... (вот тут внимание!!!!) НЕОБХОДИМО.<br><br>Кемп:<br>хорошо, что ты не сказал, что ты им еще и деньги зарабатываешь, а то она бы тебе какую-нибудь альтернативу с шубой придумала]]></description> + </item> + <item> + <guid isPermaLink="false">66fab6cb65e11c7de896d7c6840095b9a90bd70ed8d49f8fb7adc0adb9b6a43c</guid> + <link>http://bash.org.ru/quote/414930</link> + <title>Цитата #414930</title> + <pubDate>Tue, 27 Dec 2011 13:46:01 +0400</pubDate> + <description><![CDATA[ххх: Друг мне вчера рассказал. Он барменом работает в заведении. Почти в ночи к ни начали заламываться два пьяных в зюзю чувака и охрана их не пустила, так как заведение приличное. Эти два типа не нашли ничего умнее и вызвали полицию, те приехали, их самих же забрали и увезли!]]></description> + </item> + <item> + <guid isPermaLink="false">39b40cb066f0a9b64c53dbc67c1a521a1b3948a5b021efd4b1982753059aaee1</guid> + <link>http://bash.org.ru/quote/414929</link> + <title>Цитата #414929</title> + <pubDate>Tue, 27 Dec 2011 13:13:01 +0400</pubDate> + <description><![CDATA[xxx: я им звонил<br>xxx: нет у них времени на понедельник<br>yyy: печалька<br>xxx: это уже не печалька<br>xxx: это уже злостька, яростька и гневик]]></description> + </item> + <item> + <guid isPermaLink="false">4f3b79bfba6994d73b3eb21530d98188f4830c26c4f8108a6d8120a4c0b5e8fc</guid> + <link>http://bash.org.ru/quote/414928</link> + <title>Цитата #414928</title> + <pubDate>Tue, 27 Dec 2011 12:46:01 +0400</pubDate> + <description><![CDATA[Комменты к статье "Как освободиться из наручников-стяжек":<br>xxx: А вот интересно, если ты едешь в багажнике машины, что делать?<br>yyy: если ночью и в лес – то это просто плохая примета.<br>zzz: Во всём надо искать позитив! Я уверен, это будет последняя плохая примета в вашей жизни! :)]]></description> + </item> + <item> + <guid isPermaLink="false">d3162ccb1c30aa3f654c0c6b368fa9e583c020dc3d04bcc07b6c5f02eeadaaeb</guid> + <link>http://bash.org.ru/quote/414927</link> + <title>Цитата #414927</title> + <pubDate>Tue, 27 Dec 2011 12:13:01 +0400</pubDate> + <description><![CDATA[zzz: Ну вот как объяснить иностранцу, почему вилка — лежит, тарелка — стоит, а птица — сидит?<br>xxx: Вот, кстати, ноутбук: когда закрыта крышка — лежит, а когда открыта — стоит…<br>yyy: Я кажется начинаю понимать, лежит — без толку, а стоит — во имя Великой Справедливости!<br>]]></description> + </item> + <item> + <guid isPermaLink="false">69e05b82fc5a5028a65e0435a584a83ffaa44e9784c024fb30eda9b7d87f38ba</guid> + <link>http://bash.org.ru/quote/414926</link> + <title>Цитата #414926</title> + <pubDate>Tue, 27 Dec 2011 11:46:03 +0400</pubDate> + <description><![CDATA[Sovershenstvo: распечатала документ на 115 страниц. забирала из принтера. обожглась о бумагу :(<br>iNote: принтер жжот :D]]></description> + </item> + <item> + <guid isPermaLink="false">caa3a31eca89b56e3669c9910f23564e1ee2bd2c67f0be5472e206496b688609</guid> + <link>http://bash.org.ru/quote/414925</link> + <title>Цитата #414925</title> + <pubDate>Tue, 27 Dec 2011 11:45:01 +0400</pubDate> + <description><![CDATA[Из обсуждения использования акустического датчика для включения компьютера хлопком в ладоши:<br>xxx: Был такой случай.<br>xxx: Приятель сделал акустический выключатель на освещение.<br>xxx: Доставал книги с полки, стоя на табурете.<br>xxx: Упала книга, свет потух, он оступился, сломал руку.<br>xxx: Свет загорелся.]]></description> + </item> + <item> + <guid isPermaLink="false">d3db54801b673716d6b41137bbf9ca64c62a04a1211bf43ea44876c411f88728</guid> + <link>http://bash.org.ru/quote/414924</link> + <title>Цитата #414924</title> + <pubDate>Tue, 27 Dec 2011 11:13:12 +0400</pubDate> + <description><![CDATA[Обсуждаем с мужем Скайрим:<br>- Мне одной кажется, что драуги кудахтают? "Куд-ку-да", "Куд-ку-дааааа"<br>- Это еще что. Вчера скелет подходит ко мне и четко так: "пас-порт".<br>- Суровые скелеты древнескайримских пограничников. Из всего, что лепечут, можно разобрать только "паспорт" и "уходи" )]]></description> + </item> + <item> + <guid isPermaLink="false">6206e668575ef7364ca52bacdfc3d6b45da5ab80d4c69c0f1467269cec9ee6c4</guid> + <link>http://bash.org.ru/quote/414923</link> + <title>Цитата #414923</title> + <pubDate>Tue, 27 Dec 2011 11:12:01 +0400</pubDate> + <description><![CDATA[xxx: Устроил диверсию на работе - принёс ирис "Кис-Кис" (советский, железобетонный). <br>xxx: Теперь никто со мной не разговаривает. <br>xxx: ВООБЩЕ НИКТО НИ С КЕМ НЕ РАЗГОВАРИВАЕТ! )))]]></description> + </item> + <item> + <guid isPermaLink="false">47c8ec43ac12036ac0528b4b4077f8d9f80a0fe8d4aa029f8a39b7b9d4327b6f</guid> + <link>http://bash.org.ru/quote/414922</link> + <title>Цитата #414922</title> + <pubDate>Tue, 27 Dec 2011 10:45:34 +0400</pubDate> + <description><![CDATA[ххх: Я готовлюсь к зачету, только очень обдуманно. Со стороны это выглядит, будто я примеряю кирзачи]]></description> + </item> + <item> + <guid isPermaLink="false">7871bd6a1b62f062468316e82234015b27a0c448d21c39120579d4c66c1b3367</guid> + <link>http://bash.org.ru/quote/414921</link> + <title>Цитата #414921</title> + <pubDate>Tue, 27 Dec 2011 10:45:01 +0400</pubDate> + <description><![CDATA[xxx: Переехали в новый офис. Спустя три месяца обнаружили люк под ногами, а из щели свет горит. Думаем теперь - вскрывать, или не стоит.<br>yyy: Вы аккуратней там. А то посуточно дежурить заставят и циферки вводить.]]></description> + </item> + <item> + <guid isPermaLink="false">c5fcb5c4cf429099d6540943fd5f376998aee7e5ac90409e8afc3e6a61ef177f</guid> + <link>http://bash.org.ru/quote/414920</link> + <title>Цитата #414920</title> + <pubDate>Tue, 27 Dec 2011 10:13:01 +0400</pubDate> + <description><![CDATA[MG: Мама моей мамы сказала моей маме: твою ж мать за ногу! И упала.]]></description> + </item> + <item> + <guid isPermaLink="false">72838435dc732ed7410037dd17b0e38588ac91999f5f195421621a8355ded16e</guid> + <link>http://bash.org.ru/quote/414919</link> + <title>Цитата #414919</title> + <pubDate>Tue, 27 Dec 2011 09:45:45 +0400</pubDate> + <description><![CDATA[Seryozha:<br>розовый уже не модно в этом сезоне<br>стоял на остановке<br>проехал мимо розовый лимузин<br>одна фифа другой сказала, что это безвкусица<br>и это вообще уебище<br>ну и потом щемились со мной в автобус]]></description> + </item> + <item> + <guid isPermaLink="false">7b098f0d541bdc4ac0f90634a82e781d38e174ef380aa56ff6c94e69734fcde8</guid> + <link>http://bash.org.ru/quote/414918</link> + <title>Цитата #414918</title> + <pubDate>Tue, 27 Dec 2011 09:44:43 +0400</pubDate> + <description><![CDATA[Worontzoff: Избирательные участки будут оснащены камерами, куда будут помещать наблюдателей во время подсчета бюллетеней.]]></description> + </item> + <item> + <guid isPermaLink="false">5af15998f29f5f063b9ce01b54dc8d3f0b73bb7d3f877edcc5eaa7ff8f71013b</guid> + <link>http://bash.org.ru/quote/414917</link> + <title>Цитата #414917</title> + <pubDate>Tue, 27 Dec 2011 09:44:01 +0400</pubDate> + <description><![CDATA[ххх: У нас в два часа ночи пришествие Винни Пуха<br>ууу: В смысле? Мама пришла?<br>ххх: Не, Коля мед в холодильнике нашел. Ходит по квартире, нежно прижимая к себе банку. Я сегодня Пятачок]]></description> + </item> + <item> + <guid isPermaLink="false">b5a66cb535552b46159d467911174f1e4aac941c8c1d891a62619a8cdb00a3ea</guid> + <link>http://bash.org.ru/quote/414916</link> + <title>Цитата #414916</title> + <pubDate>Tue, 27 Dec 2011 09:13:02 +0400</pubDate> + <description><![CDATA[(разговор о "Лебедином Озере")<br>xxx: может быть, ты и сюжет помнишь?<br>yyy: очень смутно. Я совсем маленькая была, когда его в последний раз показывали...]]></description> + </item> + <item> + <guid isPermaLink="false">9e007ee3d9984de85c727f6885f913891b11676f10b053ba8a51bec6fba0e23c</guid> + <link>http://bash.org.ru/quote/414915</link> + <title>Цитата #414915</title> + <pubDate>Tue, 27 Dec 2011 08:46:11 +0400</pubDate> + <description><![CDATA[xxx: привет<br>xxx: как служба?<br>yyy: как у служебной собаки<br>yyy: целый день спасаю мир, а вечером обещали покормить]]></description> + </item> + <item> + <guid isPermaLink="false">3dc9ddc2fad185181993315c5c3a404405ab704423d3c24bd9213dcb23bad2eb</guid> + <link>http://bash.org.ru/quote/414914</link> + <title>Цитата #414914</title> + <pubDate>Tue, 27 Dec 2011 08:45:01 +0400</pubDate> + <description><![CDATA[ххх: нет, я скорее пойду убивать, чем красть<br>ууу: странная мораль, что так?<br>ххх: я человек честный, а потому злой]]></description> + </item> + <item> + <guid isPermaLink="false">61e793991c5dea07ea5a5afa2bbc899e49c23d8a0d8a1f48918915129a13a35c</guid> + <link>http://bash.org.ru/quote/414913</link> + <title>Цитата #414913</title> + <pubDate>Tue, 27 Dec 2011 08:12:04 +0400</pubDate> + <description><![CDATA[Речь об аудионаркотиках:<br><br>ххх: Да фигня это всё!<br>yyy: Ну почему фигня, есть же звуки, которые как-то воздействуют на психику человека...<br>ххх: Да, на меня звук дрели так воздействует! Сразу хочется пойти и убить кого-нибудь!]]></description> + </item> + <item> + <guid isPermaLink="false">ed2932ac0768ceb5ec1a155d4b039fb2cba050296935d7852baccb238ada1819</guid> + <link>http://bash.org.ru/quote/414912</link> + <title>Цитата #414912</title> + <pubDate>Tue, 27 Dec 2011 08:11:31 +0400</pubDate> + <description><![CDATA[<xxx> готовили обед, полез в холодильник<br><xxx> оказывается, вчера я невольно купил продуктовый набор "Злые птицы": ноги куриные, грудка индейки, свиные эскалопы]]></description> + </item> + <item> + <guid isPermaLink="false">1989a43d3a03de416d16788589caaa318ece611245b328df9b1567c13205cf70</guid> + <link>http://bash.org.ru/quote/414911</link> + <title>Цитата #414911</title> + <pubDate>Tue, 27 Dec 2011 08:11:01 +0400</pubDate> + <description><![CDATA[xxx: скользко на улице, даже собака гулять не хочет<br>yyy: ей-то что, у неё ж полный привод]]></description> + </item> + <item> + <guid isPermaLink="false">c207fd198261176146047e54ccfff9058582e311ab402e32dfe67837c3e61e64</guid> + <link>http://bash.org.ru/quote/414910</link> + <title>Цитата #414910</title> + <pubDate>Mon, 26 Dec 2011 13:12:49 +0400</pubDate> + <description><![CDATA[Мама, купившая своему ребенку чехол к мобильному телефону, принесла его в магазин, чтобы вернуть продавцу.<br><br>П: На что жалуетесь?<br>М: Ребенок отказывается пользоваться чехлом.<br>П: Почему? Что не устраивает?<br>М: Мы заказывали чехол с человеком-пауком. На чехле надпись SPIDER-MAN. Так вот, когда застегиваем молнию, становится не видно букву S.]]></description> + </item> + <item> + <guid isPermaLink="false">245f34c6c6da77a68ba258f717177e3ecb27865bd7cd72c5966a9bae3d68cdab</guid> + <link>http://bash.org.ru/quote/414909</link> + <title>Цитата #414909</title> + <pubDate>Mon, 26 Dec 2011 13:11:56 +0400</pubDate> + <description><![CDATA[xxx: Дай фломастер, палец покрасить!<br>yyy: Зачем?<br>xxx: Иду в гости, а у меня носок дырявый!]]></description> + </item> + <item> + <guid isPermaLink="false">b9674cdf642593a5c193c3262031513822c50bcb950170cc07aadfb7fddc2649</guid> + <link>http://bash.org.ru/quote/414908</link> + <title>Цитата #414908</title> + <pubDate>Mon, 26 Dec 2011 13:11:02 +0400</pubDate> + <description><![CDATA[* В чат входит Вазелин.<br>Вазелин: студенты, я пришел<br>Вазелин: кому сколько?<br>Вазелин: пора бы уже заказывать]]></description> + </item> + <item> + <guid isPermaLink="false">f5e384c1050a5d6df2cbe999fbae0e082deda5e89839ad0a913b490b3fada8c0</guid> + <link>http://bash.org.ru/quote/414907</link> + <title>Цитата #414907</title> + <pubDate>Mon, 26 Dec 2011 12:46:14 +0400</pubDate> + <description><![CDATA[ххх: гуляли с ребенком в парке, кормили уточек<br>ххх: сначала их было несколько штук, потом подплыли еще, некоторые пикировали с воздуха)))<br>ххх: приличное количество собралось, в общем, некоторые вылезли на берег и подошли к нам почти вплотную<br>ххх: в какой-то момент мне стало стремно, что булка кончается]]></description> + </item> + <item> + <guid isPermaLink="false">71aba0126bc495b6efcd494e8b95c11b4e27fadb96f8fa31475db7aa85c7f1a4</guid> + <link>http://bash.org.ru/quote/414906</link> + <title>Цитата #414906</title> + <pubDate>Mon, 26 Dec 2011 12:45:14 +0400</pubDate> + <description><![CDATA[xxx: "Если вы читали роман Жюля Верна или хотя бы смотрели фильм с одноимённым названием, то будете не то чтобы разочарованны, вы, мягко говоря, будете шокированы увиденным, начиная с первых секунд фильма"<br><br>xxx: кто бы блять мог подумать, что именно так начинается рецензия к фильму Мушкетёры...]]></description> + </item> + <item> + <guid isPermaLink="false">3d3b28ac59593143fe32ecbb4c5df048f39482f38d336d6dc4ad119ffbeea78b</guid> + <link>http://bash.org.ru/quote/414905</link> + <title>Цитата #414905</title> + <pubDate>Mon, 26 Dec 2011 12:44:01 +0400</pubDate> + <description><![CDATA[xxx: а ведущий программист - это такой программист, который может решить любую поставленную задачу его профиля самостоятельно<br>yyy: но не хочет )]]></description> + </item> + <item> + <guid isPermaLink="false">2931790e946054e81789e63e91d5dc9237bf492d3f26c6d7bc530b59ef2672fe</guid> + <link>http://bash.org.ru/quote/414904</link> + <title>Цитата #414904</title> + <pubDate>Mon, 26 Dec 2011 12:13:01 +0400</pubDate> + <description><![CDATA[xxx: Погодите, на андроиде проходит авторизация по распознаванию лица? То есть, если мне набьют морду, я не смогу в скорую позвонить?<br>yyy: Боюсь, что набитие морды и отъём смартфона - события сильно скоррелированные, так что Ваша проблема несколько надуманна. :)]]></description> + </item> + <item> + <guid isPermaLink="false">5ee6027cf4758aa3989a3552db60d21acfc4bce66518265e9137abc3ce1fc3c7</guid> + <link>http://bash.org.ru/quote/414903</link> + <title>Цитата #414903</title> + <pubDate>Mon, 26 Dec 2011 11:46:01 +0400</pubDate> + <description><![CDATA[leon:<br>Сегодня отвожу ребенка в школу, а там стенд новый повесили с фотографиями учителей. Озаглавлено гордо "Сердце нашей школы!". Рядом с фотографией трудовика в берете, маркером стрелочка и надпись "это печень".]]></description> + </item> + <item> + <guid isPermaLink="false">a47fdbea7602bfa4df71054ed3d391d00a1c0dd13f8adaa983bb5f93e5ac3d90</guid> + <link>http://bash.org.ru/quote/414902</link> + <title>Цитата #414902</title> + <pubDate>Mon, 26 Dec 2011 11:13:01 +0400</pubDate> + <description><![CDATA[xxx: безработный студент всем своим видом доказывает нам, что минимального размера оплаты труда не существует.]]></description> + </item> + <item> + <guid isPermaLink="false">bc8b51fdf44d63edc888ad063db2fa66d1ad92c48d999408fff00a8cbec8e4ae</guid> + <link>http://bash.org.ru/quote/414901</link> + <title>Цитата #414901</title> + <pubDate>Mon, 26 Dec 2011 10:46:12 +0400</pubDate> + <description><![CDATA[Psihodel: сегодня с утра пришла смс "Мам кинь утром на мтс ххх-хх-хх-хх 950р. Мне не звони. Позже объясню." Через час мне жена сообщает, что она беременна. Теперь я понял, почему не надо звонить. Вот лет через 5 узнаю, зачем ему надо было 950р.]]></description> + </item> + <item> + <guid isPermaLink="false">e5174f709f5372c11eaf170b06efe25bf23fd3a65dbc9669e7b55b83ad598340</guid> + <link>http://bash.org.ru/quote/414900</link> + <title>Цитата #414900</title> + <pubDate>Mon, 26 Dec 2011 10:45:01 +0400</pubDate> + <description><![CDATA[xxx: Завтра встреча одногруппников. в 16:00 в Пабе на Таганке. Ну чего, ты будешь ?<br>yyy: Водку<br>yyy: Думаю я ответил сразу на два вопроса]]></description> + </item> + <item> + <guid isPermaLink="false">0f5c7eaee411484a37e8071fb60030eb902f42a26969363233116d425faec446</guid> + <link>http://bash.org.ru/quote/414899</link> + <title>Цитата #414899</title> + <pubDate>Mon, 26 Dec 2011 10:12:34 +0400</pubDate> + <description><![CDATA[Настоящий радиолюбитель, открыв спичечный коробок и обнаружив там спички, смотрит на них С УДИВЛЕНИЕМ]]></description> + </item> + <item> + <guid isPermaLink="false">1941999057f97da275dbaee167f5314de54a3de0f1ccc0f25c983cfe08845e71</guid> + <link>http://bash.org.ru/quote/414898</link> + <title>Цитата #414898</title> + <pubDate>Mon, 26 Dec 2011 10:12:01 +0400</pubDate> + <description><![CDATA[xxx: Если в вашей жизни постоянно происходит какая-то херня, то стоит задуматься, а не вы ли та самая херня.]]></description> + </item> + <item> + <guid isPermaLink="false">1493df9ec128867419e1c9e0f8adf1e7c73eaecba9ba81e51395778f2c599faf</guid> + <link>http://bash.org.ru/quote/414897</link> + <title>Цитата #414897</title> + <pubDate>Mon, 26 Dec 2011 09:46:15 +0400</pubDate> + <description><![CDATA[ххх: плохая это идея, с детскими домашними праздниками.<br>ууу: да почему? у меня знакомые этим каждые праздники занимаются. вполне неплохо для подработки, весело + сразу деньги в кармане.<br>ххх: пару лет назад мы с подругой тоже раздобыли новогодних костюмов, написали веселый сценарий и развеслили объявления: "Волшебное новогоднее приключение от сестричек-Снегурочек в вашем доме".<br>ххх: ах вы мои неиспорченные!<br>ууу: ага, все правильно понял.]]></description> + </item> + <item> + <guid isPermaLink="false">507045f7af9d1dbc586b1ea528ecc9edb09ea5a1f35dab1141afc0dee491c7f2</guid> + <link>http://bash.org.ru/quote/414896</link> + <title>Цитата #414896</title> + <pubDate>Mon, 26 Dec 2011 09:45:01 +0400</pubDate> + <description><![CDATA[CKuB: ЗАЯ, бля! Переложить все говно на мою полку - это не то, что я имел в виду, прося навести порядок у себя на полке!]]></description> + </item> + <item> + <guid isPermaLink="false">e4f30a21e7ffa737cc164ea71ddf1fe24a2c87b269a1b47db1dbf591ebe3e843</guid> + <link>http://bash.org.ru/quote/414895</link> + <title>Цитата #414895</title> + <pubDate>Mon, 26 Dec 2011 09:12:45 +0400</pubDate> + <description><![CDATA[xxx: Админская примета.<br>xxx: Если 12-летний сын начальника идет в серверную - к большой беде.]]></description> + </item> + <item> + <guid isPermaLink="false">0ac1bcd3e77c115764edf2fef78568d7e88f0dcfa2830ebff7a4a67ca246192b</guid> + <link>http://bash.org.ru/quote/414894</link> + <title>Цитата #414894</title> + <pubDate>Mon, 26 Dec 2011 09:11:52 +0400</pubDate> + <description><![CDATA[Vegyja: романтика - чем ее меньше, тем лучше. если парень только и делает, что страдает такой фигней... вопрос возникает: а мужчина ли он?<br>Telecantrelem: А если предложить девушке бутерброд с колбасой - это романтика?<br>1143r: Telecantrelem, в определенных обстоятельствах это очень даже романтично. Например, Она выпила стакан самогону, а закусить нечем. А тут ты с бутером. Опа! На, ешь!]]></description> + </item> + <item> + <guid isPermaLink="false">0b0d1ae0708037d49d4edd71c8e1d7a488907c379787b7827be44ba7adab6cef</guid> + <link>http://bash.org.ru/quote/414893</link> + <title>Цитата #414893</title> + <pubDate>Mon, 26 Dec 2011 09:11:01 +0400</pubDate> + <description><![CDATA[Из объяснительной записки:<br><br>12.12 на складе №4 был разгружен ламинат "..." с большим количеством брака. Пачки были отсортированы мной и подписаны, хорошие пачки "Х", а плохие пачки "П". При возврате товара поставщику, начальник склада ошибочно посчитал, что "Х" означает "хуёвые", а "П" означает "пиздатые", что в корне меняло ситуацию. Грузчики его поддержали и на возврат был отгружен качественный товар. Виновным в данном инциденте себя не признаю, услуги транспортной компании оплачивать отказываюсь.]]></description> + </item> + <item> + <guid isPermaLink="false">4caeec3b83a23c4ef7e6822f1b9151c62dd719538f9719fd9dc49079edf12869</guid> + <link>http://bash.org.ru/quote/414892</link> + <title>Цитата #414892</title> + <pubDate>Mon, 26 Dec 2011 08:46:01 +0400</pubDate> + <description><![CDATA[Лена: что делать, если кот спит, я одна дома, а на кухне грохот?<br>Олег: кинь туда кота)]]></description> + </item> + <item> + <guid isPermaLink="false">533d63288a28594dd6858f5eb212c9fd62c95da02ef6c532902585a525f78b2e</guid> + <link>http://bash.org.ru/quote/414891</link> + <title>Цитата #414891</title> + <pubDate>Mon, 26 Dec 2011 08:13:01 +0400</pubDate> + <description><![CDATA[он: Расскажи мне сказку<br>она: Жила-была некая функция эф от икс, и была она дифференцируема на всей эпсилон-окрестности некой точки икс нулевое<br>она: У меня численные методы )<br>она: Извини )]]></description> + </item> + <item> + <guid isPermaLink="false">92cf655d26cbe5d10b1af532ab4abb491a0d74ae9759cb81cd6dd4bb84a1d2bc</guid> + <link>http://bash.org.ru/quote/414890</link> + <title>Цитата #414890</title> + <pubDate>Sun, 25 Dec 2011 09:13:01 +0400</pubDate> + <description><![CDATA[смс из очереди к врачу<br><br>ххх: Мама, за час приняли только двух больных и они до сих пор там. передо мной 12 злых и опасных бабушек, которые уже успели закатить два скандала с применением двери и грубой физической силы тем, кто пытался пройти без очереди. Подчёркиваю: ПЫТАЛСЯ! Мне есть смысл сидеть дальше?]]></description> + </item> + </channel> +</rss>
\ 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<opml version="1.0"> + <head> + <title>BasiL: подписки в Google Reader</title> + </head> + <body> + <outline text="ithappens" title="ithappens" type="rss" + xmlUrl="http://ithappens.ru/rss" htmlUrl="http://ithappens.ru/"/> + <outline title="Computerra" text="Computerra"> + <outline title="My_Test" text="My_Test"> + <outline text="bash" title="bash" type="rss" + xmlUrl="http://bash.im/rss" htmlUrl="http://bash.im/"/> + </outline> + <outline text="inside" title="inside" type="rss" + xmlUrl="http://www.ctinside.com/feeds/posts/default" htmlUrl="http://www.ctinside.com/"/> + <outline text="Компьюлента" title="Компьюлента" type="rss" + xmlUrl="http://www.compulenta.ru/rss.xml" htmlUrl="http://www.compulenta.ru"/> + <outline text="Статьи Компьютерры" + title="Статьи Компьютерры" type="rss" + xmlUrl="http://www.computerra.ru/rss.xml" htmlUrl="http://www.computerra.ru"/> + </outline> + <outline title="Soft" text="Soft"> + <outline + text="Downloads for project cm9-wildfire-s on Google Code" + title="Downloads for project cm9-wildfire-s on Google Code" + type="rss" + xmlUrl="https://code.google.com/feeds/p/cm9-wildfire-s/downloads/basic" htmlUrl="http://code.google.com/p/cm9-wildfire-s/downloads/list"/> + <outline text="Downloads for project uawks on Google Code" + title="Downloads for project uawks on Google Code" + type="rss" + xmlUrl="http://code.google.com/feeds/p/uawks/downloads/basic" htmlUrl="http://code.google.com/p/uawks/downloads/list"/> + <outline + text="Issue updates for project uawks on Google Code" + title="Issue updates for project uawks on Google Code" + type="rss" + xmlUrl="http://code.google.com/feeds/p/uawks/issueupdates/basic" htmlUrl="http://code.google.com/p/uawks/issues/list"/> + <outline + text="Mercurial commits to project uawks on Google Code" + title="Mercurial commits to project uawks on Google Code" + type="rss" + xmlUrl="http://code.google.com/feeds/p/uawks/hgchanges/basic" htmlUrl="http://code.google.com/p/uawks/source/list"/> + <outline text="Punk" title="Punk" type="rss" + xmlUrl="http://www.punksoftware.com/rss" htmlUrl="http://punklabs.com/rss"/> + <outline text="Tomato by Shibby" title="Tomato by Shibby" + type="rss" xmlUrl="http://tomato.groov.pl/?feed=rss2" htmlUrl="http://tomato.groov.pl"/> + <outline text="TOR" title="TOR" type="rss" + xmlUrl="http://rss.gmane.org/gmane.network.onion-routing.announce" htmlUrl="http://permalink.gmane.org/gmane.network.onion-routing.announce"/> + <outline text="VideoLAN" title="VideoLAN" type="rss" + xmlUrl="http://www.videolan.org/videolan-news.rss" htmlUrl="http://www.videolan.org/"/> + </outline> + <outline title="Miranda" text="Miranda"> + <outline text="Dev Blog" title="Dev Blog" type="rss" + xmlUrl="http://feeds.miranda-im.org/mirandadevjournal" htmlUrl="http://www.miranda-im.org"/> + <outline text="FLNew" title="FLNew" type="rss" + xmlUrl="http://feeds.miranda-im.org/mirandalast10add" htmlUrl="http://addons.miranda-im.org"/> + <outline text="FLupd" title="FLupd" type="rss" + xmlUrl="http://feeds.miranda-im.org/mirandalast10upd" htmlUrl="http://addons.miranda-im.org"/> + <outline text="mir-rl project updates - Google Code" + title="mir-rl project updates - Google Code" type="rss" + xmlUrl="http://code.google.com/feeds/p/mir-rl/updates/basic" htmlUrl="http://code.google.com/p/mir-rl/updates/list"/> + <outline text="Miranda IM" title="Miranda IM" type="rss" + xmlUrl="http://feeds.feedburner.com/mirandaannouncements" htmlUrl="http://www.miranda-im.org"/> + <outline text="Miranda IM Community" + title="Miranda IM Community" type="rss" + xmlUrl="http://community.livejournal.com/ru_mirandaim/data/rss" htmlUrl="http://ru-mirandaim.livejournal.com/"/> + <outline + text="Miranda-Planet - ваша планета Miranda IM ( билды, сборки, скины, плагины, русификация, WiKi, Форум )" + title="Miranda-Planet - ваша планета Miranda IM ( билды, сборки, скины, плагины, русификация, WiKi, Форум )" + type="rss" xmlUrl="http://miranda-planet.com/rss.xml" htmlUrl="http://miranda-planet.com/"/> + <outline text="ruCommunity" title="ruCommunity" type="rss" + xmlUrl="http://forum.miranda.im/external.php?type=RSS2" htmlUrl="http://forum.miranda.im"/> + </outline> + <outline title="Habr" text="Habr"> + <outline text="Android" title="Android" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/android/" htmlUrl="http://habrahabr.ru/rss/hub/android/"/> + <outline text="Design" title="Design" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/design/" htmlUrl="http://habrahabr.ru/rss/hub/design/"/> + <outline text="DSLR" title="DSLR" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/dslr/" htmlUrl="http://habrahabr.ru/rss/blog/dslr/"/> + <outline text="Future" title="Future" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/the_future_is_here/" htmlUrl="http://habrahabr.ru/rss/hub/the_future_is_here/"/> + <outline text="Google" title="Google" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/google/" htmlUrl="http://habrahabr.ru/rss/hub/google/"/> + <outline text="Hardware" title="Hardware" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/hardware/" htmlUrl="http://habrahabr.ru/rss/hub/hardware/"/> + <outline text="IM" title="IM" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/im/" htmlUrl="http://habrahabr.ru/rss/hub/im/"/> + <outline text="Jabber" title="Jabber" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/jabber/" htmlUrl="http://habrahabr.ru/rss/blogs/jabber/"/> + <outline text="Miranda" title="Miranda" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/miranda/" htmlUrl="http://habrahabr.ru/rss/blogs/miranda/"/> + <outline text="Netbook" title="Netbook" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/netbook/" htmlUrl="http://habrahabr.ru/rss/hub/netbook/"/> + <outline text="OldThings" title="OldThings" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/retro/" htmlUrl="http://habrahabr.ru/rss/blogs/retro/"/> + <outline text="Robots" title="Robots" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/robot/" htmlUrl="http://habrahabr.ru/rss/hub/robot/"/> + <outline text="sciense" title="sciense" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/popular_science/" htmlUrl="http://habrahabr.ru/rss/hub/popular_science/"/> + <outline text="TC" title="TC" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/totalcmd/" htmlUrl="http://habrahabr.ru/rss/blogs/totalcmd/"/> + <outline text="Telecom" title="Telecom" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/telecom/" htmlUrl="http://habrahabr.ru/rss/hub/telecom/"/> + <outline text="Win7" title="Win7" type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/windows7/" htmlUrl="http://habrahabr.ru/rss/blogs/windows7/"/> + <outline text="Хабрахабр / Биотехнологии / Захабренные" + title="Хабрахабр / Биотехнологии / Захабренные" + type="rss" + xmlUrl="http://habrahabr.ru/rss/blogs/biotech/" htmlUrl="http://habrahabr.ru/rss/hub/biotech/"/> + <outline text="Хабрахабр: Метки / wzor.net" + title="Хабрахабр: Метки / wzor.net" type="rss" + xmlUrl="http://habrahabr.ru/rss/tag/wzor.net/" htmlUrl="http://habrahabr.ru/rss/tag/wzor.net/"/> + </outline> + <outline title="News" text="News"> + <outline text="2КОМ (новости)" title="2КОМ (новости)" + type="rss" + xmlUrl="http://www.2kom.ru/home/news/-rss.xml" htmlUrl="http://www.2kom.ru/home/news.html"/> + <outline text="4PDA" title="4PDA" type="rss" + xmlUrl="http://4pda.ru/feed/" htmlUrl="http://4pda.ru/"/> + <outline text="FotoTips.ru" title="FotoTips.ru" type="rss" + xmlUrl="http://feeds.feedburner.com/fototipsru" htmlUrl="http://fototips.ru"/> + <outline text="LifeHack.ru" title="LifeHack.ru" type="rss" + xmlUrl="http://www.lifehack.ru/rss/" htmlUrl="http://lifehack.ru"/> + <outline text="MForum.ru" title="MForum.ru" type="rss" + xmlUrl="http://www.mforum.ru/rss/news.xml" htmlUrl="http://www.mforum.ru/"/> + <outline text="Overclockers.ru" title="Overclockers.ru" + type="rss" + xmlUrl="http://www.overclockers.ru/rss/all.rss" htmlUrl="http://www.overclockers.ru"/> + <outline text="revolver.ru" title="revolver.ru" type="rss" + xmlUrl="http://revolver.ru/rss" htmlUrl="http://revolver.ru/"/> + <outline text="Самый сок!" title="Самый сок!" type="rss" + xmlUrl="http://ibigdan.livejournal.com/data/rss" htmlUrl="http://ibigdan.livejournal.com/"/> + <outline text="Танки Онлайн" title="Танки Онлайн" type="rss" + xmlUrl="http://blog.tankionline.com/feed/" htmlUrl="http://news.tankionline.com"/> + <outline text="Фергана.Ру" title="Фергана.Ру" type="rss" + xmlUrl="http://news.ferghana.ru/news.xml" htmlUrl="http://www.fergananews.com"/> + </outline> + <outline title="TC" text="TC"> + <outline text="en.totalcmd.pl" title="en.totalcmd.pl" + type="rss" xmlUrl="http://en.totalcmd.pl/rss" htmlUrl="http://en.totalcmd.pl"/> + <outline text="tc" title="tc" type="rss" + xmlUrl="http://wincmd.ru/rss.xml" htmlUrl="http://wincmd.ru"/> + </outline> + <outline title="Science" text="Science"> + <outline text="Astronet" title="Astronet" type="rss" + xmlUrl="http://www.astronet.ru/db/rss.xml" htmlUrl="http://www.astronet.ru/"/> + <outline text="COPAH.info - Наука. Сибирь. Общество" + title="COPAH.info - Наука. Сибирь. Общество" type="rss" + xmlUrl="http://www.copah.info/rss" htmlUrl="http://www.copah.info/rss"/> + <outline text="LHC" title="LHC" type="rss" + xmlUrl="http://fulltextrssfeed.com/elementy.ru/rss/news/LHC" htmlUrl="http://elementy.ru/LHC/news"/> + <outline text="Membrana.ru" title="Membrana.ru" type="rss" + xmlUrl="http://ftr.fivefilters.org/makefulltextfeed.php?url=http%3A%2F%2Fwww.membrana.ru%2Fexport%2Frss.xml&max=1" htmlUrl="http://www.membrana.ru"/> + <outline text="Иванов" title="Иванов" type="rss" + xmlUrl="http://igorivanov.blogspot.com/feeds/posts/default" htmlUrl="http://igorivanov.blogspot.com/"/> + <outline text="НаукаИжизнь" title="НаукаИжизнь" type="rss" + xmlUrl="http://fulltextrssfeed.com/www.nkj.ru/rss/iblock_rss_31.xml" htmlUrl="http://www.nkj.ru"/> + <outline text="Элементы" title="Элементы" type="rss" + xmlUrl="http://fulltextrssfeed.com/elementy.ru/rss/news" htmlUrl="http://elementy.ru/"/> + </outline> + <outline title="Moviez" text="Moviez"> + <outline text="Twitter / kuraj_bambey" + title="Twitter / kuraj_bambey" type="rss" + xmlUrl="http://twitter.com/statuses/user_timeline/44353505.rss" htmlUrl="http://twitter.com/kuraj_bambey"/> + <outline text="Мосенька" title="Мосенька" type="rss" + xmlUrl="http://feed.rutracker.org/atom/u/0/26/3796626.atom" htmlUrl="http://rutracker.org/forum/profile.php?mode=viewprofile&u=3796626"/> + </outline> + </body> +</opml> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectName>Proto_NewsAggregator</ProjectName> + <ProjectGuid>{B97882DC-7462-41DB-A390-BDFCE5295265}</ProjectGuid> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(ProjectDir)..\..\..\build\vc.common\icons.props" /> + </ImportGroup> +</Project>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\..\build\vc.common\common.filters" /> +</Project>
\ No newline at end of file diff --git a/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico b/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico Binary files differnew file mode 100644 index 0000000000..b4fba290e7 --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/res/Offline.ico diff --git a/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico b/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico Binary files differnew file mode 100644 index 0000000000..75c9ff1aa3 --- /dev/null +++ b/protocols/NewsAggregator/proto_newsaggregator/res/Online.ico 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 |