From c7ed9c6df72dbea9ae132b0c482ced8eb140c8c8 Mon Sep 17 00:00:00 2001 From: thunder2 Date: Tue, 4 Sep 2012 23:53:04 +0000 Subject: [PATCH] FeedReader plugin - reserved service id - reworked error codes - added xpath manipulation and basic gui elements in preview dialog git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-gxs-b1@5514 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/serialiser/rsserviceids.h | 1 + plugins/FeedReader/FeedReader.pro | 6 +- plugins/FeedReader/FeedReaderPlugin.h | 3 +- plugins/FeedReader/gui/AddFeedDialog.cpp | 37 +- plugins/FeedReader/gui/AddFeedDialog.h | 4 + .../FeedReader/gui/FeedReaderStringDefs.cpp | 52 +- plugins/FeedReader/gui/FeedReaderStringDefs.h | 1 + plugins/FeedReader/gui/PreviewFeedDialog.cpp | 447 +++++++++++++-- plugins/FeedReader/gui/PreviewFeedDialog.h | 21 +- plugins/FeedReader/gui/PreviewFeedDialog.ui | 417 ++++++++++---- plugins/FeedReader/interface/rsFeedReader.h | 34 +- plugins/FeedReader/services/p3FeedReader.cc | 154 +++--- plugins/FeedReader/services/p3FeedReader.h | 17 +- .../FeedReader/services/p3FeedReaderThread.cc | 509 ++++++++++++++---- .../FeedReader/services/p3FeedReaderThread.h | 27 +- .../FeedReader/services/rsFeedReaderItems.cc | 37 +- .../FeedReader/services/rsFeedReaderItems.h | 10 +- plugins/FeedReader/util/HTMLWrapper.cpp | 17 +- plugins/FeedReader/util/HTMLWrapper.h | 4 + plugins/FeedReader/util/XMLWrapper.cpp | 58 +- plugins/FeedReader/util/XMLWrapper.h | 16 +- plugins/FeedReader/util/XPathWrapper.cpp | 102 ++++ plugins/FeedReader/util/XPathWrapper.h | 51 ++ 23 files changed, 1595 insertions(+), 430 deletions(-) create mode 100644 plugins/FeedReader/util/XPathWrapper.cpp create mode 100644 plugins/FeedReader/util/XPathWrapper.h diff --git a/libretroshare/src/serialiser/rsserviceids.h b/libretroshare/src/serialiser/rsserviceids.h index f5cbf16ab..c6cdd6870 100644 --- a/libretroshare/src/serialiser/rsserviceids.h +++ b/libretroshare/src/serialiser/rsserviceids.h @@ -65,6 +65,7 @@ const uint16_t RS_SERVICE_TYPE_STATUS = 0xf020; const uint16_t RS_SERVICE_TYPE_PLUGIN_ARADO_ID = 0x0401; const uint16_t RS_SERVICE_TYPE_PLUGIN_QCHESS_ID = 0x0402; +const uint16_t RS_SERVICE_TYPE_PLUGIN_FEEDREADER = 0x0403; /****************** BELOW ARE ONLY THEORETICAL (CAN BE CHANGED) *****/ diff --git a/plugins/FeedReader/FeedReader.pro b/plugins/FeedReader/FeedReader.pro index 3974acbae..450ea6d66 100644 --- a/plugins/FeedReader/FeedReader.pro +++ b/plugins/FeedReader/FeedReader.pro @@ -14,7 +14,8 @@ SOURCES = FeedReaderPlugin.cpp \ gui/FeedReaderStringDefs.cpp \ util/CURLWrapper.cpp \ util/XMLWrapper.cpp \ - util/HTMLWrapper.cpp + util/HTMLWrapper.cpp \ + util/XPathWrapper.cpp HEADERS = FeedReaderPlugin.h \ interface/rsFeedReader.h \ @@ -29,7 +30,8 @@ HEADERS = FeedReaderPlugin.h \ gui/FeedReaderStringDefs.h \ util/CURLWrapper.h \ util/XMLWrapper.h \ - util/HTMLWrapper.h + util/HTMLWrapper.h \ + util/XPathWrapper.h FORMS = gui/FeedReaderDialog.ui \ gui/AddFeedDialog.ui \ diff --git a/plugins/FeedReader/FeedReaderPlugin.h b/plugins/FeedReader/FeedReaderPlugin.h index dffcfa7b4..2c4fd5d81 100644 --- a/plugins/FeedReader/FeedReaderPlugin.h +++ b/plugins/FeedReader/FeedReaderPlugin.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include "services/p3FeedReader.h" @@ -33,7 +34,7 @@ class FeedReaderPlugin: public RsPlugin public: FeedReaderPlugin(); - virtual uint16_t rs_service_id() const { return RS_PKT_TYPE_FEEDREADER_CONFIG; } + virtual uint16_t rs_service_id() const { return RS_SERVICE_TYPE_PLUGIN_FEEDREADER; } virtual RsPQIService *rs_pqi_service() const; virtual void stop(); diff --git a/plugins/FeedReader/gui/AddFeedDialog.cpp b/plugins/FeedReader/gui/AddFeedDialog.cpp index f4f4064b1..fcfb3c947 100644 --- a/plugins/FeedReader/gui/AddFeedDialog.cpp +++ b/plugins/FeedReader/gui/AddFeedDialog.cpp @@ -27,6 +27,7 @@ #include "ui_AddFeedDialog.h" #include "PreviewFeedDialog.h" #include "FeedReaderStringDefs.h" +#include "gui/settings/rsharesettings.h" #include "retroshare/rsforums.h" @@ -36,7 +37,7 @@ bool sortForumInfo(const ForumInfo& info1, const ForumInfo& info2) } AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent) - : QDialog(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::AddFeedDialog) + : QDialog(parent, Qt::Window), mFeedReader(feedReader), mNotify(notify), ui(new Ui::AddFeedDialog) { ui->setupUi(this); @@ -93,13 +94,37 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, validate(); ui->urlLineEdit->setFocus(); + + /* load settings */ + processSettings(true); } AddFeedDialog::~AddFeedDialog() { + /* save settings */ + processSettings(false); + delete ui; } +void AddFeedDialog::processSettings(bool load) +{ + Settings->beginGroup(QString("AddFeedDialog")); + + if (load) { + // load settings + QByteArray geometry = Settings->value("Geometry").toByteArray(); + if (!geometry.isEmpty()) { + restoreGeometry(geometry); + } + } else { + // save settings + Settings->setValue("Geometry", saveGeometry()); + } + + Settings->endGroup(); +} + void AddFeedDialog::authenticationToggled() { bool checked = ui->useAuthenticationCheckBox->isChecked(); @@ -234,6 +259,9 @@ bool AddFeedDialog::fillFeed(const std::string &feedId) ui->useStandardStorageTimeCheckBox->setChecked(feedInfo.flag.standardStorageTime); ui->storageTimeSpinBox->setValue(feedInfo.storageTime / (60 * 60 *24)); + + mXPathsToUse = feedInfo.xpathsToUse; + mXPathsToRemove = feedInfo.xpathsToRemove; } return true; @@ -274,6 +302,9 @@ void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo) feedInfo.flag.standardStorageTime = ui->useStandardStorageTimeCheckBox->isChecked(); feedInfo.storageTime = ui->storageTimeSpinBox->value() * 60 *60 * 24; + + feedInfo.xpathsToUse = mXPathsToUse; + feedInfo.xpathsToRemove = mXPathsToRemove; } void AddFeedDialog::createFeed() @@ -310,5 +341,7 @@ void AddFeedDialog::preview() getFeedInfo(feedInfo); PreviewFeedDialog dialog(mFeedReader, mNotify, feedInfo, this); - dialog.exec(); + if (dialog.exec() == QDialog::Accepted) { + dialog.getXPaths(mXPathsToUse, mXPathsToRemove); + } } diff --git a/plugins/FeedReader/gui/AddFeedDialog.h b/plugins/FeedReader/gui/AddFeedDialog.h index 92841c93b..44066c522 100644 --- a/plugins/FeedReader/gui/AddFeedDialog.h +++ b/plugins/FeedReader/gui/AddFeedDialog.h @@ -55,6 +55,7 @@ private slots: void preview(); private: + void processSettings(bool load); void getFeedInfo(FeedInfo &feedInfo); RsFeedReader *mFeedReader; @@ -62,6 +63,9 @@ private: std::string mFeedId; std::string mParentId; + std::list mXPathsToUse; + std::list mXPathsToRemove; + Ui::AddFeedDialog *ui; }; diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.cpp b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp index 37dc1dc78..d7cd8d502 100644 --- a/plugins/FeedReader/gui/FeedReaderStringDefs.cpp +++ b/plugins/FeedReader/gui/FeedReaderStringDefs.cpp @@ -76,55 +76,73 @@ QString FeedReaderStringDefs::workState(FeedInfo::WorkState state) QString FeedReaderStringDefs::errorString(const FeedInfo &feedInfo) { - QString errorState; - switch (feedInfo.errorState) { + return errorString(feedInfo.errorState, feedInfo.errorString); +} + +QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, const std::string &errorString) +{ + QString errorText; + switch (errorState) { case RS_FEED_ERRORSTATE_OK: break; /* download */ case RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR: - errorState = QApplication::translate("FeedReaderStringDefs", "Internal download error"); + errorText = QApplication::translate("FeedReaderStringDefs", "Internal download error"); break; case RS_FEED_ERRORSTATE_DOWNLOAD_ERROR: - errorState = QApplication::translate("FeedReaderStringDefs", "Download error"); + errorText = QApplication::translate("FeedReaderStringDefs", "Download error"); break; case RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE: - errorState = QApplication::translate("FeedReaderStringDefs", "Unknown content type"); + errorText = QApplication::translate("FeedReaderStringDefs", "Unknown content type"); break; case RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND: - errorState = QApplication::translate("FeedReaderStringDefs", "Download not found"); + errorText = QApplication::translate("FeedReaderStringDefs", "Download not found"); break; case RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE: - errorState = QApplication::translate("FeedReaderStringDefs", "Unknown response code"); + errorText = QApplication::translate("FeedReaderStringDefs", "Unknown response code"); break; /* process */ case RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR: - errorState = QApplication::translate("FeedReaderStringDefs", "Internal process error"); + errorText = QApplication::translate("FeedReaderStringDefs", "Internal process error"); break; case RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT: - errorState = QApplication::translate("FeedReaderStringDefs", "Unknown XML format"); + errorText = QApplication::translate("FeedReaderStringDefs", "Unknown XML format"); break; case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE: - errorState = QApplication::translate("FeedReaderStringDefs", "Can't create forum"); + errorText = QApplication::translate("FeedReaderStringDefs", "Can't create forum"); break; case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND: - errorState = QApplication::translate("FeedReaderStringDefs", "Forum not found"); + errorText = QApplication::translate("FeedReaderStringDefs", "Forum not found"); break; case RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN: - errorState = QApplication::translate("FeedReaderStringDefs", "You are not admin of the forum"); + errorText = QApplication::translate("FeedReaderStringDefs", "You are not admin of the forum"); break; case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS: - errorState = QApplication::translate("FeedReaderStringDefs", "The forum is no anonymous forum"); + errorText = QApplication::translate("FeedReaderStringDefs", "The forum is no anonymous forum"); + break; + + case RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR: + errorText = QApplication::translate("FeedReaderStringDefs", "Can't read html"); + break; + case RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR: + errorText = QApplication::translate("FeedReaderStringDefs", "Internal XPath error"); + break; + case RS_FEED_ERRORSTATE_PROCESS_XPATH_WRONG_EXPRESSION: + errorText = QApplication::translate("FeedReaderStringDefs", "Wrong XPath expression"); + break; + case RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT: + errorText = QApplication::translate("FeedReaderStringDefs", "Empty XPath result"); break; default: - errorState = QApplication::translate("FeedReaderStringDefs", "Unknown error"); + errorText = QApplication::translate("FeedReaderStringDefs", "Unknown error"); } - if (!feedInfo.errorString.empty()) { - errorState += QString(" (%1)").arg(QString::fromUtf8(feedInfo.errorString.c_str())); + if (!errorString.empty()) { + errorText += QString(" (%1)").arg(QString::fromUtf8(errorString.c_str())); } - return errorState; + return errorText; } diff --git a/plugins/FeedReader/gui/FeedReaderStringDefs.h b/plugins/FeedReader/gui/FeedReaderStringDefs.h index d94103cfc..13f9afa30 100644 --- a/plugins/FeedReader/gui/FeedReaderStringDefs.h +++ b/plugins/FeedReader/gui/FeedReaderStringDefs.h @@ -34,6 +34,7 @@ public: static bool showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text); static QString workState(FeedInfo::WorkState state); static QString errorString(const FeedInfo &feedInfo); + static QString errorString(RsFeedReaderErrorState errorState, const std::string &errorString); }; #endif diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.cpp b/plugins/FeedReader/gui/PreviewFeedDialog.cpp index 76bd2c7da..947057798 100644 --- a/plugins/FeedReader/gui/PreviewFeedDialog.cpp +++ b/plugins/FeedReader/gui/PreviewFeedDialog.cpp @@ -19,13 +19,16 @@ * Boston, MA 02110-1301, USA. ****************************************************************/ -#include +//#include +#include +#include #include "PreviewFeedDialog.h" #include "ui_PreviewFeedDialog.h" #include "FeedReaderNotify.h" #include "FeedReaderStringDefs.h" #include "util/HandleRichText.h" +#include "gui/settings/rsharesettings.h" #include "interface/rsFeedReader.h" #include "retroshare/rsiface.h" @@ -138,33 +141,67 @@ //} PreviewFeedDialog::PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, const FeedInfo &feedInfo, QWidget *parent) : - QDialog(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::PreviewFeedDialog) + QDialog(parent, Qt::Window), mFeedReader(feedReader), mNotify(notify), ui(new Ui::PreviewFeedDialog) { ui->setupUi(this); ui->feedNameLabel->clear(); - ui->feedInfoLabel->clear(); + ui->useXPathCheckBox->setChecked(true); /* connect signals */ connect(ui->previousPushButton, SIGNAL(clicked()), this, SLOT(previousMsg())); connect(ui->nextPushButton, SIGNAL(clicked()), this, SLOT(nextMsg())); - connect(ui->documentButton, SIGNAL(toggled(bool)), this, SLOT(showDocumentFrame(bool))); + connect(ui->closeStructureButton, SIGNAL(clicked()), this, SLOT(showStructureFrame())); + connect(ui->structureButton, SIGNAL(toggled(bool)), this, SLOT(showStructureFrame(bool))); + connect(ui->xpathPushButton, SIGNAL(toggled(bool)), this, SLOT(showXPathFrame(bool))); + connect(ui->useXPathCheckBox, SIGNAL(toggled(bool)), this, SLOT(fillStructureTree())); + connect(ui->xpathUseListWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(xpathListCustomPopupMenu(QPoint))); + connect(ui->xpathRemoveListWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(xpathListCustomPopupMenu(QPoint))); + connect(ui->xpathUseListWidget->itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(xpathCloseEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); + connect(ui->xpathRemoveListWidget->itemDelegate(), SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), this, SLOT(xpathCloseEditor(QWidget*,QAbstractItemDelegate::EndEditHint))); connect(mNotify, SIGNAL(notifyFeedChanged(QString,int)), this, SLOT(feedChanged(QString,int))); connect(mNotify, SIGNAL(notifyMsgChanged(QString,QString,int)), this, SLOT(msgChanged(QString,QString,int))); // ui->documentTreeWidget->setItemDelegate(new PreviewItemDelegate(ui->documentTreeWidget)); - showDocumentFrame(false); + ui->structureFrame->hide(); - if (!mFeedReader->addPreviewFeed(feedInfo, mFeedId)) { - setInfo(tr("Cannot create preview")); + if (mFeedReader->addPreviewFeed(feedInfo, mFeedId)) { + setFeedInfo(""); + } else { + setFeedInfo(tr("Cannot create preview")); + } + setXPathInfo(""); + showXPathFrame(true); + + /* fill xpath expressions */ + QListWidgetItem *item; + std::string xpath; + foreach(xpath, feedInfo.xpathsToUse){ + item = new QListWidgetItem(QString::fromUtf8(xpath.c_str())); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->xpathUseListWidget->addItem(item); + } + foreach(xpath, feedInfo.xpathsToRemove){ + item = new QListWidgetItem(QString::fromUtf8(xpath.c_str())); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->xpathRemoveListWidget->addItem(item); } updateMsgCount(); + + ui->xpathUseListWidget->installEventFilter(this); + ui->xpathRemoveListWidget->installEventFilter(this); + + /* load settings */ + processSettings(true); } PreviewFeedDialog::~PreviewFeedDialog() { + /* save settings */ + processSettings(false); + disconnect(mNotify); disconnect(mNotify); @@ -175,6 +212,51 @@ PreviewFeedDialog::~PreviewFeedDialog() delete ui; } +void PreviewFeedDialog::processSettings(bool load) +{ + Settings->beginGroup(QString("PreviewFeedDialog")); + + if (load) { + // load settings + QByteArray geometry = Settings->value("Geometry").toByteArray(); + if (!geometry.isEmpty()) { + restoreGeometry(geometry); + } + } else { + // save settings + Settings->setValue("Geometry", saveGeometry()); + } + + Settings->endGroup(); +} + +bool PreviewFeedDialog::eventFilter(QObject *obj, QEvent *event) +{ + long todo_here; + + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent) { + if (keyEvent->key() == Qt::Key_Delete) { + /* Delete pressed */ + if (obj == ui->xpathUseListWidget || obj == ui->xpathRemoveListWidget) { + QListWidget *listWidget = dynamic_cast(obj); + if (listWidget) { + QListWidgetItem *item = listWidget->currentItem(); + if (item) { + delete(item); + processXPath(); + } + return true; // eat event + } + } + } + } + } + /* pass the event on to the parent class */ + return QDialog::eventFilter(obj, event); +} + void PreviewFeedDialog::feedChanged(const QString &feedId, int type) { if (feedId.isEmpty()) { @@ -251,22 +333,139 @@ void PreviewFeedDialog::msgChanged(const QString &feedId, const QString &msgId, updateMsgCount(); } -void PreviewFeedDialog::showDocumentFrame(bool show) +void PreviewFeedDialog::showStructureFrame(bool show) { - ui->documentFrame->setVisible(show); - ui->documentButton->setChecked(show); + ui->structureButton->setChecked(show); + ui->structureFrame->setVisible(show); if (show) { - ui->documentButton->setToolTip(tr("Hide tree")); - ui->documentButton->setIcon(QIcon(":images/hide_toolbox_frame.png")); - - fillDocumentTree(); - } else { - ui->documentButton->setToolTip(tr("Show tree")); - ui->documentButton->setIcon(QIcon(":images/show_toolbox_frame.png")); + fillStructureTree(); } } +void PreviewFeedDialog::showXPathFrame(bool show) +{ + ui->xpathFrame->setVisible(show); + + if (show) { + ui->xpathPushButton->setToolTip(tr("Hide XPath expressions")); + ui->xpathPushButton->setIcon(QIcon(":images/show_toolbox_frame.png")); + } else { + ui->xpathPushButton->setToolTip(tr("Show XPath expressions")); + ui->xpathPushButton->setIcon(QIcon(":images/hide_toolbox_frame.png")); + } +} + +void PreviewFeedDialog::xpathListCustomPopupMenu(QPoint /*point*/) +{ + QListWidgetItem *item = NULL; + + if (sender() == ui->xpathUseListWidget) { + item = ui->xpathUseListWidget->currentItem(); + } else if (sender() == ui->xpathRemoveListWidget) { + item = ui->xpathRemoveListWidget->currentItem(); + } else { + return; + } + + QMenu contextMnu(this); + + QAction *action = contextMnu.addAction(QIcon(), tr("Add"), this, SLOT(addXPath())); + action->setData(QVariant::fromValue(sender())); + + action = contextMnu.addAction(QIcon(), tr("Edit"), this, SLOT(editXPath())); + action->setData(QVariant::fromValue(sender())); + if (!item) { + action->setEnabled(false); + } + + action = contextMnu.addAction(QIcon(), tr("Delete"), this, SLOT(removeXPath())); + action->setData(QVariant::fromValue(sender())); + if (!item) { + action->setEnabled(false); + } + + contextMnu.exec(QCursor::pos()); +} + + +void PreviewFeedDialog::xpathCloseEditor(QWidget */*editor*/, QAbstractItemDelegate::EndEditHint /*hint*/) +{ + processXPath(); +} + +void PreviewFeedDialog::addXPath() +{ + QAction *action = dynamic_cast(sender()); + if (!action) { + return; + } + + QObject *source = action->data().value(); + + QListWidget *listWidget; + if (source == ui->xpathUseListWidget) { + listWidget = ui->xpathUseListWidget; + } else if (source == ui->xpathRemoveListWidget) { + listWidget = ui->xpathRemoveListWidget; + } else { + return; + } + + QListWidgetItem *item = new QListWidgetItem(); + item->setFlags(item->flags() | Qt::ItemIsEditable); + listWidget->addItem(item); + + listWidget->editItem(item); +} + +void PreviewFeedDialog::editXPath() +{ + QAction *action = dynamic_cast(sender()); + if (!action) { + return; + } + + QObject *source = action->data().value(); + + QListWidget *listWidget; + if (source == ui->xpathUseListWidget) { + listWidget = ui->xpathUseListWidget; + } else if (source == ui->xpathRemoveListWidget) { + listWidget = ui->xpathRemoveListWidget; + } else { + return; + } + + listWidget->editItem(listWidget->currentItem()); +} + +void PreviewFeedDialog::removeXPath() +{ + QAction *action = dynamic_cast(sender()); + if (!action) { + return; + } + + QObject *source = action->data().value(); + + QListWidget *listWidget; + if (source == ui->xpathUseListWidget) { + listWidget = ui->xpathUseListWidget; + } else if (source == ui->xpathRemoveListWidget) { + listWidget = ui->xpathRemoveListWidget; + } else { + return; + } + + QListWidgetItem *item = listWidget->currentItem(); + if (item) { + delete(item); + } + + processXPath(); +} + int PreviewFeedDialog::getMsgPos() { int pos = -1; @@ -281,14 +480,18 @@ int PreviewFeedDialog::getMsgPos() return pos; } -void PreviewFeedDialog::setInfo(const QString &info) +void PreviewFeedDialog::setFeedInfo(const QString &info) { ui->feedInfoLabel->setText(info); - - ui->infoLabel->setVisible(!info.isEmpty()); ui->feedInfoLabel->setVisible(!info.isEmpty()); } +void PreviewFeedDialog::setXPathInfo(const QString &info) +{ + ui->xpathInfoLabel->setText(info); + ui->xpathInfoLabel->setVisible(!info.isEmpty()); +} + void PreviewFeedDialog::fillFeedInfo(const FeedInfo &feedInfo) { QString name = feedInfo.name.empty() ? tr("No name") : QString::fromUtf8(feedInfo.name.c_str()); @@ -299,7 +502,7 @@ void PreviewFeedDialog::fillFeedInfo(const FeedInfo &feedInfo) } ui->feedNameLabel->setText(name); - setInfo(FeedReaderStringDefs::errorString(feedInfo)); + setFeedInfo(FeedReaderStringDefs::errorString(feedInfo)); } void PreviewFeedDialog::previousMsg() @@ -344,24 +547,23 @@ void PreviewFeedDialog::updateMsg() ui->msgTitle->clear(); ui->msgText->clear(); mDescription.clear(); + mDescriptionXPath.clear(); return; } - mDescription = msgInfo.description; - QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(mDescription.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); - - ui->msgText->setHtml(msgTxt); ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str())); - ui->documentTreeWidget->clear(); - fillDocumentTree(); + /* store description */ + mDescription = msgInfo.description; + + /* process xpath */ + processXPath(); } -static void examineChildElements(QTreeWidget *treeWidget, HTMLWrapper &html, xmlNodePtr node, QTreeWidgetItem *parentItem) +static void buildNodeText(HTMLWrapper &html, xmlNodePtr node, QString &text) { - QTreeWidgetItem *item = new QTreeWidgetItem; - QString text; - if (node->type == XML_ELEMENT_NODE) { + switch (node->type) { + case XML_ELEMENT_NODE: text = QString("<%1 ").arg(QString::fromUtf8(html.nodeName(node).c_str())); for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) { @@ -372,48 +574,148 @@ static void examineChildElements(QTreeWidget *treeWidget, HTMLWrapper &html, xml text += QString("%1=\"%2\" ").arg(QString::fromUtf8(html.attrName(attr).c_str()), value); } text = text.trimmed() + ">"; - } else { - std::string content; - if (html.getContent(node, content)) { - text = QString::fromUtf8(content.c_str()); - } else { - text = QApplication::translate("PreviewFeedDialog", "Error getting content"); + + if (node->children && !node->children->next && node->children->type == XML_TEXT_NODE) { + /* only one text node as child */ + std::string content; + if (html.getContent(node->children, content)) { + text += QString::fromUtf8(content.c_str()); + } else { + text += QApplication::translate("PreviewFeedDialog", "Error getting content"); + } + text += QString("<%1>").arg(QString::fromUtf8(html.nodeName(node).c_str())); + + xmlUnlinkNode(node->children); + xmlFreeNode(node->children); } + break; + case XML_TEXT_NODE: + case XML_COMMENT_NODE: + { + if (node->type == XML_COMMENT_NODE) { + text = ""; + } + } + break; + case XML_ATTRIBUTE_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + break; + } +} + +static void examineChildElements(QTreeWidget *treeWidget, HTMLWrapper &html, QList &nodes, QTreeWidgetItem *parentItem) +{ + int childIndex = 0; + int childCount; + + QList > nodeItems; + foreach (xmlNodePtr node, nodes) { + QString text; + buildNodeText(html, node, text); + + QList itemsToDelete; + QTreeWidgetItem *item = NULL; + + childCount = parentItem->childCount(); + for (int index = childIndex; index < childCount; ++index) { + QTreeWidgetItem *childItem = parentItem->child(index); + if (childItem->text(0) == text) { + /* reuse item */ + item = childItem; + break; + } + itemsToDelete.push_back(childItem); + } + + if (item) { + /* delete old items */ + foreach (QTreeWidgetItem *item, itemsToDelete) { + delete(item); + } + ++childIndex; + } else { + item = new QTreeWidgetItem; + item->setText(0, text); + parentItem->insertChild(childIndex, item); + item->setExpanded(true); + + ++childIndex; + } + + nodeItems.push_back(QPair(node, item)); + } + + /* delete not used items */ + while (childIndex < parentItem->childCount()) { + delete(parentItem->child(childIndex)); + } + + QList >::iterator nodeItem; + for (nodeItem = nodeItems.begin(); nodeItem != nodeItems.end(); ++nodeItem) { + QList childNodes; + for (xmlNodePtr childNode = nodeItem->first->children; childNode; childNode = childNode->next) { + childNodes.push_back(childNode); + } + examineChildElements(treeWidget, html, childNodes, nodeItem->second); } - item->setText(0, text); - parentItem->addChild(item); // QLabel *label = new QLabel(text); // label->setTextFormat(Qt::PlainText); // label->setWordWrap(true); // treeWidget->setItemWidget(item, 0, label); - - item->setExpanded(true); - - for (xmlNodePtr child = node->children; child; child = child->next) { - examineChildElements(treeWidget, html, child, item); - } } -void PreviewFeedDialog::fillDocumentTree() +void PreviewFeedDialog::fillStructureTree() { - if (!ui->documentTreeWidget->isVisible()) { + if (!ui->structureTreeWidget->isVisible()) { return; } - if (ui->documentTreeWidget->topLevelItemCount() > 0) { +// if (ui->structureTreeWidget->topLevelItemCount() > 0) { +// return; +// } + + if (mDescriptionXPath.empty()) { + ui->structureTreeWidget->clear(); return; } - if (mDescription.empty()) { - return; - } + bool useXPath = ui->useXPathCheckBox->isChecked(); HTMLWrapper html; - if (!html.readHTML(mDescription.c_str(), "")) { + if (!html.readHTML(useXPath ? mDescriptionXPath.c_str() : mDescription.c_str(), "")) { QTreeWidgetItem *item = new QTreeWidgetItem; item->setText(0, tr("Error parsing document")); - ui->documentTreeWidget->addTopLevelItem(item); + ui->structureTreeWidget->addTopLevelItem(item); return; } @@ -423,5 +725,42 @@ void PreviewFeedDialog::fillDocumentTree() return; } - examineChildElements(ui->documentTreeWidget, html, root, ui->documentTreeWidget->invisibleRootItem()); + QList nodes; + nodes.push_back(root); + examineChildElements(ui->structureTreeWidget, html, nodes, ui->structureTreeWidget->invisibleRootItem()); + ui->structureTreeWidget->resizeColumnToContents(0); +} + +void PreviewFeedDialog::getXPaths(std::list &xpathsToUse, std::list &xpathsToRemove) +{ + int row; + int rowCount = ui->xpathUseListWidget->count(); + for (row = 0; row < rowCount; ++row) { + xpathsToUse.push_back(ui->xpathUseListWidget->item(row)->text().toUtf8().constData()); + } + + rowCount = ui->xpathRemoveListWidget->count(); + for (row = 0; row < rowCount; ++row) { + xpathsToRemove.push_back(ui->xpathRemoveListWidget->item(row)->text().toUtf8().constData()); + } +} + +void PreviewFeedDialog::processXPath() +{ + std::list xpathsToUse; + std::list xpathsToRemove; + + getXPaths(xpathsToUse, xpathsToRemove); + + mDescriptionXPath = mDescription; + std::string errorString; + RsFeedReaderErrorState result = mFeedReader->processXPath(xpathsToUse, xpathsToRemove, mDescriptionXPath, errorString); + setXPathInfo(FeedReaderStringDefs::errorString(result, errorString)); + + /* fill message */ + QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8(mDescriptionXPath.c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); + ui->msgText->setHtml(msgTxt); + + /* fill structure */ + fillStructureTree(); } diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.h b/plugins/FeedReader/gui/PreviewFeedDialog.h index a801577bc..d6c7e3da9 100644 --- a/plugins/FeedReader/gui/PreviewFeedDialog.h +++ b/plugins/FeedReader/gui/PreviewFeedDialog.h @@ -59,22 +59,36 @@ public: PreviewFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, const FeedInfo &feedInfo, QWidget *parent = 0); ~PreviewFeedDialog(); + void getXPaths(std::list &xpathsToUse, std::list &xpathsToRemove); + +protected: + bool eventFilter(QObject *obj, QEvent *ev); + private slots: void previousMsg(); void nextMsg(); - void showDocumentFrame(bool show); + void showStructureFrame(bool show = false); + void showXPathFrame(bool show); + void xpathListCustomPopupMenu(QPoint point); + void xpathCloseEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); + void addXPath(); + void editXPath(); + void removeXPath(); + void fillStructureTree(); /* FeedReaderNotify */ void feedChanged(const QString &feedId, int type); void msgChanged(const QString &feedId, const QString &msgId, int type); private: + void processSettings(bool load); int getMsgPos(); - void setInfo(const QString &info); + void setFeedInfo(const QString &info); + void setXPathInfo(const QString &info); void fillFeedInfo(const FeedInfo &feedInfo); void updateMsgCount(); void updateMsg(); - void fillDocumentTree(); + void processXPath(); RsFeedReader *mFeedReader; FeedReaderNotify *mNotify; @@ -82,6 +96,7 @@ private: std::string mMsgId; std::list mMsgIds; std::string mDescription; + std::string mDescriptionXPath; Ui::PreviewFeedDialog *ui; }; diff --git a/plugins/FeedReader/gui/PreviewFeedDialog.ui b/plugins/FeedReader/gui/PreviewFeedDialog.ui index 9b5d92570..21a2a89a5 100644 --- a/plugins/FeedReader/gui/PreviewFeedDialog.ui +++ b/plugins/FeedReader/gui/PreviewFeedDialog.ui @@ -6,8 +6,8 @@ 0 0 - 500 - 350 + 800 + 783 @@ -45,29 +45,103 @@ - - - - Information: - - - + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 118 + 116 + 108 + + + + + + - Information + Feed information true + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 118 + 116 + 108 + + + + + + + + XPath information + + + - + - + Qt::Horizontal @@ -119,7 +193,7 @@ - + Qt::Horizontal @@ -131,6 +205,16 @@ + + + + Structure + + + true + + + @@ -173,92 +257,241 @@ background: white;} - - - 0 + + + + 0 + 0 + - - + + QFrame::StyledPanel + + + QFrame::Raised + + + Qt::Vertical + + + false + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + - - - - 14 - 31 - + + + 0 - - - 14 - 31 - - - - - - - - 16 - 31 - - - - true - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 16 + 16 + + + + QToolButton +{ + border-image: url(:/images/closenormal.png) +} + +QToolButton:hover +{ +border-image: url(:/images/closehover.png) +} + +QToolButton:pressed { +border-image: url(:/images/closepressed.png) +} + + + ... + + + true + + + + - - - Qt::Vertical - - - - 12 - 329 - - - + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + Qt::Horizontal + + + false + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 0 + + + + + Use XPath + + + + + + + true + + + false + + + false + + + + 1 + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 0 + + + + + XPath use + + + + + + + Qt::CustomContextMenu + + + + + + + XPath remove + + + + + + + Qt::CustomContextMenu + + + + + + + + + + + + + + 14 + 31 + + + + + 14 + 31 + + + + true + + + + + + + Qt::Vertical + + + + 13 + 13 + + + + + + + - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - - - true - - - false - - - - 1 - - - - - - - - - - - + + diff --git a/plugins/FeedReader/interface/rsFeedReader.h b/plugins/FeedReader/interface/rsFeedReader.h index 4ea33cac4..dc3efd1b3 100644 --- a/plugins/FeedReader/interface/rsFeedReader.h +++ b/plugins/FeedReader/interface/rsFeedReader.h @@ -27,22 +27,27 @@ #include enum RsFeedReaderErrorState { - RS_FEED_ERRORSTATE_OK = 0, + RS_FEED_ERRORSTATE_OK = 0, /* download */ - RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR = 1, - RS_FEED_ERRORSTATE_DOWNLOAD_ERROR = 2, - RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE = 3, - RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND = 4, - RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE = 5, + RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR = 1, + RS_FEED_ERRORSTATE_DOWNLOAD_ERROR = 2, + RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE = 3, + RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND = 4, + RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE = 5, /* process */ - RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR = 50, - RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT = 51, - RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100, - RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND = 101, - RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN = 102, - RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS = 103 + RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR = 50, + RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT = 51, + RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND = 101, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN = 102, + RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_ANONYMOUS = 103, + + RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR = 150, + RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR = 151, + RS_FEED_ERRORSTATE_PROCESS_XPATH_WRONG_EXPRESSION = 152, + RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT = 153 }; @@ -111,6 +116,9 @@ public: RsFeedReaderErrorState errorState; std::string errorString; + std::list xpathsToUse; + std::list xpathsToRemove; + struct { bool folder : 1; bool infoFromFeed : 1; @@ -192,6 +200,8 @@ public: virtual bool getFeedMsgIdList(const std::string &feedId, std::list &msgIds) = 0; virtual bool processFeed(const std::string &feedId) = 0; virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read) = 0; + + virtual RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString) = 0; }; #endif diff --git a/plugins/FeedReader/services/p3FeedReader.cc b/plugins/FeedReader/services/p3FeedReader.cc index 6aaad1003..f4940048d 100644 --- a/plugins/FeedReader/services/p3FeedReader.cc +++ b/plugins/FeedReader/services/p3FeedReader.cc @@ -37,7 +37,7 @@ RsFeedReader *rsFeedReader = NULL; *********/ p3FeedReader::p3FeedReader(RsPluginHandler* pgHandler) - : RsPQIService(RS_PKT_TYPE_FEEDREADER_CONFIG, CONFIG_TYPE_FEEDREADER, 5, pgHandler), + : RsPQIService(RS_SERVICE_TYPE_PLUGIN_FEEDREADER, CONFIG_TYPE_FEEDREADER, 5, pgHandler), mFeedReaderMtx("p3FeedReader"), mDownloadMutex("p3FeedReaderDownload"), mProcessMutex("p3FeedReaderProcess"), mPreviewMutex("p3FeedReaderPreview") { mNextFeedId = 1; @@ -88,6 +88,9 @@ static void feedToInfo(const RsFeedReaderFeed *feed, FeedInfo &info) info.errorState = feed->errorState; info.errorString = feed->errorString; + info.xpathsToUse = feed->xpathsToUse.ids; + info.xpathsToRemove = feed->xpathsToRemove.ids; + info.flag.folder = (feed->flag & RS_FEED_FLAG_FOLDER); info.flag.infoFromFeed = (feed->flag & RS_FEED_FLAG_INFO_FROM_FEED); info.flag.standardStorageTime = (feed->flag & RS_FEED_FLAG_STANDARD_STORAGE_TIME); @@ -141,6 +144,9 @@ static void infoToFeed(const FeedInfo &info, RsFeedReaderFeed *feed, bool add) feed->forumId = info.forumId; } + feed->xpathsToUse.ids = info.xpathsToUse; + feed->xpathsToRemove.ids = info.xpathsToRemove; + // feed->preview = info.flag.preview; uint32_t oldFlag = feed->flag; @@ -512,11 +518,11 @@ void p3FeedReader::deleteAllMsgs_locked(RsFeedReaderFeed *fi) } std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ++msgIt) { delete(msgIt->second); } - fi->mMsgs.clear(); + fi->msgs.clear(); } bool p3FeedReader::removeFeed(const std::string &feedId) @@ -712,8 +718,8 @@ bool p3FeedReader::getMsgInfo(const std::string &feedId, const std::string &msgI RsFeedReaderFeed *fi = feedIt->second; std::map::iterator msgIt; - msgIt = fi->mMsgs.find(msgId); - if (msgIt == fi->mMsgs.end()) { + msgIt = fi->msgs.find(msgId); + if (msgIt == fi->msgs.end()) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::getMsgInfo - msg " << msgId << " not found" << std::endl; #endif @@ -744,8 +750,8 @@ bool p3FeedReader::removeMsg(const std::string &feedId, const std::string &msgId changed = !fi->preview; std::map::iterator msgIt; - msgIt = fi->mMsgs.find(msgId); - if (msgIt == fi->mMsgs.end()) { + msgIt = fi->msgs.find(msgId); + if (msgIt == fi->msgs.end()) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::removeMsg - msg " << msgId << " not found" << std::endl; #endif @@ -789,8 +795,8 @@ bool p3FeedReader::removeMsgs(const std::string &feedId, const std::list::const_iterator idIt; for (idIt = msgIds.begin(); idIt != msgIds.end(); ++idIt) { std::map::iterator msgIt; - msgIt = fi->mMsgs.find(*idIt); - if (msgIt == fi->mMsgs.end()) { + msgIt = fi->msgs.find(*idIt); + if (msgIt == fi->msgs.end()) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::removeMsgs - msg " << *idIt << " not found" << std::endl; #endif @@ -842,7 +848,7 @@ bool p3FeedReader::getMessageCount(const std::string &feedId, uint32_t *msgCount RsFeedReaderFeed *fi = feedIt->second; std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ++msgIt) { RsFeedReaderMsg *mi = msgIt->second; if (mi->flag & RS_FEEDMSG_FLAG_DELETED) { @@ -872,7 +878,7 @@ bool p3FeedReader::getFeedMsgList(const std::string &feedId, std::listsecond; std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ++msgIt) { RsFeedReaderMsg *mi = msgIt->second; if (mi->flag & RS_FEEDMSG_FLAG_DELETED) { @@ -902,7 +908,7 @@ bool p3FeedReader::getFeedMsgIdList(const std::string &feedId, std::listsecond; std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ++msgIt) { RsFeedReaderMsg *mi = msgIt->second; if (mi->flag & RS_FEEDMSG_FLAG_DELETED) { @@ -1060,8 +1066,8 @@ bool p3FeedReader::setMessageRead(const std::string &feedId, const std::string & RsFeedReaderFeed *fi = feedIt->second; std::map::iterator msgIt; - msgIt = fi->mMsgs.find(msgId); - if (msgIt == fi->mMsgs.end()) { + msgIt = fi->msgs.find(msgId); + if (msgIt == fi->msgs.end()) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::setMessageRead - msg " << msgId << " not found" << std::endl; #endif @@ -1092,6 +1098,11 @@ bool p3FeedReader::setMessageRead(const std::string &feedId, const std::string & return true; } +RsFeedReaderErrorState p3FeedReader::processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString) +{ + return p3FeedReaderThread::processXPath(xpathsToUse, xpathsToRemove, description, errorString); +} + /***************************************************************************/ /****************************** p3Service **********************************/ /***************************************************************************/ @@ -1185,7 +1196,7 @@ void p3FeedReader::cleanFeeds() uint32_t removedMsgs = 0; std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ) { RsFeedReaderMsg *mi = msgIt->second; if (mi->flag & RS_FEEDMSG_FLAG_DELETED) { @@ -1193,7 +1204,7 @@ void p3FeedReader::cleanFeeds() removedMsgIds.push_back(std::pair (fi->feedId, mi->msgId)); delete(mi); std::map::iterator deleteIt = msgIt++; - fi->mMsgs.erase(deleteIt); + fi->msgs.erase(deleteIt); ++removedMsgs; continue; } @@ -1274,7 +1285,7 @@ bool p3FeedReader::saveList(bool &cleanup, std::list & saveData) saveData.push_back(fi); std::map::iterator it2; - for (it2 = fi->mMsgs.begin(); it2 != fi->mMsgs.end(); ++it2) { + for (it2 = fi->msgs.begin(); it2 != fi->msgs.end(); ++it2) { saveData.push_back(it2->second); } } @@ -1376,7 +1387,7 @@ bool p3FeedReader::loadList(std::list& load) delete it1->second; continue; } - it2->second->mMsgs[it1->first] = it1->second; + it2->second->msgs[it1->first] = it1->second; if (msgId + 1 > mNextMsgId) { mNextMsgId = msgId + 1; } @@ -1496,7 +1507,7 @@ void p3FeedReader::onDownloadSuccess(const std::string &feedId, const std::strin } } -void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &errorString) +void p3FeedReader::onDownloadError(const std::string &feedId, RsFeedReaderErrorState result, const std::string &errorString) { { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1516,28 +1527,7 @@ void p3FeedReader::onDownloadError(const std::string &feedId, p3FeedReaderThread fi->lastUpdate = time(NULL); fi->content.clear(); - switch (result) { - case p3FeedReaderThread::DOWNLOAD_SUCCESS: - /* this should not happen */ - std::cerr << "p3FeedReader::onDownloadError - success given as error" << std::endl; - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR; - break; - case p3FeedReaderThread::DOWNLOAD_ERROR: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_ERROR; - break; - case p3FeedReaderThread::DOWNLOAD_UNKNOWN_CONTENT_TYPE: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE; - break; - case p3FeedReaderThread::DOWNLOAD_NOT_FOUND: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND; - break; - case p3FeedReaderThread::DOWNLOAD_UNKOWN_RESPONSE_CODE: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE; - break; - default: - fi->errorState = RS_FEED_ERRORSTATE_DOWNLOAD_INTERNAL_ERROR; - } - + fi->errorState = result; fi->errorString = errorString; #ifdef FEEDREADER_DEBUG @@ -1611,7 +1601,7 @@ bool p3FeedReader::getFeedToProcess(RsFeedReaderFeed &feed, const std::string &n return true; } -bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::list &msgs) +void p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::list &msgs) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - feed " << feedId << " got " << msgs.size() << " messages" << std::endl; @@ -1627,7 +1617,7 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::onProcessSuccess_filterMsg - feed " << feedId << " not found" << std::endl; #endif - return false; + return; } RsFeedReaderFeed *fi = it->second; @@ -1637,14 +1627,14 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li RsFeedReaderMsg *miNew = *newMsgIt; /* search for existing msg */ std::map::iterator msgIt; - for (msgIt = fi->mMsgs.begin(); msgIt != fi->mMsgs.end(); ++msgIt) { + for (msgIt = fi->msgs.begin(); msgIt != fi->msgs.end(); ++msgIt) { RsFeedReaderMsg *mi = msgIt->second; if (mi->title == miNew->title && mi->link == miNew->link && mi->author == miNew->author) { /* msg exist */ break; } } - if (msgIt != fi->mMsgs.end()) { + if (msgIt != fi->msgs.end()) { /* msg exists */ delete(miNew); newMsgIt = msgs.erase(newMsgIt); @@ -1660,11 +1650,9 @@ bool p3FeedReader::onProcessSuccess_filterMsg(const std::string &feedId, std::li IndicateConfigChanged(); } } - - return true; } -void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs, bool single) +void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, std::list &msgs, bool single) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << feedId << " got " << msgs.size() << " messages" << std::endl; @@ -1691,7 +1679,7 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool resu bool forum = (fi->flag & RS_FEED_FLAG_FORUM) && !fi->preview; RsFeedReaderErrorState errorState = RS_FEED_ERRORSTATE_OK; - if (result && forum && !msgs.empty()) { + if (forum && !msgs.empty()) { if (fi->forumId.empty()) { /* create new forum */ std::wstring forumName; @@ -1751,35 +1739,33 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool resu uint32_t newMsgs = 0; #endif - if (result) { - std::list::iterator newMsgIt; - for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { - RsFeedReaderMsg *miNew = *newMsgIt; - /* add new msg */ - if (fi->preview) { - rs_sprintf(miNew->msgId, "preview%d", mNextPreviewMsgId--); - } else { - rs_sprintf(miNew->msgId, "%lu", mNextMsgId++); - } - if (forum) { - miNew->flag = RS_FEEDMSG_FLAG_DELETED; - forumMsgs.push_back(*miNew); -// miNew->description.clear(); - } else { - miNew->flag = RS_FEEDMSG_FLAG_NEW; - addedMsgs.push_back(miNew->msgId); - } - fi->mMsgs[miNew->msgId] = miNew; - newMsgIt = msgs.erase(newMsgIt); + std::list::iterator newMsgIt; + for (newMsgIt = msgs.begin(); newMsgIt != msgs.end(); ) { + RsFeedReaderMsg *miNew = *newMsgIt; + /* add new msg */ + if (fi->preview) { + rs_sprintf(miNew->msgId, "preview%d", mNextPreviewMsgId--); + } else { + rs_sprintf(miNew->msgId, "%lu", mNextMsgId++); + } + if (forum) { + miNew->flag = RS_FEEDMSG_FLAG_DELETED; + forumMsgs.push_back(*miNew); +// miNew->description.clear(); + } else { + miNew->flag = RS_FEEDMSG_FLAG_NEW; + addedMsgs.push_back(miNew->msgId); + } + fi->msgs[miNew->msgId] = miNew; + newMsgIt = msgs.erase(newMsgIt); #ifdef FEEDREADER_DEBUG - ++newMsgs; -#endif - } -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << fi->feedId << " (" << fi->name << ") added " << newMsgs << "/" << msgs.size() << " messages" << std::endl; + ++newMsgs; #endif } +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReader::onProcessSuccess_addMsgs - feed " << fi->feedId << " (" << fi->name << ") added " << newMsgs << "/" << msgs.size() << " messages" << std::endl; +#endif } if (!single) { @@ -1833,7 +1819,7 @@ void p3FeedReader::onProcessSuccess_addMsgs(const std::string &feedId, bool resu } } -void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result, const std::string &errorString) +void p3FeedReader::onProcessError(const std::string &feedId, RsFeedReaderErrorState result, const std::string &errorString) { { RsStackMutex stack(mFeedReaderMtx); /******* LOCK STACK MUTEX *********/ @@ -1853,23 +1839,7 @@ void p3FeedReader::onProcessError(const std::string &feedId, p3FeedReaderThread: fi->lastUpdate = time(NULL); fi->content.clear(); - long todo; // sort error codes - switch (result) { - case p3FeedReaderThread::PROCESS_SUCCESS: - /* this should not happen */ - std::cerr << "p3FeedReader::onProcessError - success given as error" << std::endl; - fi->errorState = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; - break; - case p3FeedReaderThread::PROCESS_ERROR_INIT: - fi->errorState = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; - break; - case p3FeedReaderThread::PROCESS_UNKNOWN_FORMAT: - fi->errorState = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT; - break; - default: - fi->errorState = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; - } - + fi->errorState = result; fi->errorString = errorString; #ifdef FEEDREADER_DEBUG diff --git a/plugins/FeedReader/services/p3FeedReader.h b/plugins/FeedReader/services/p3FeedReader.h index 2bced3c0f..34901620e 100644 --- a/plugins/FeedReader/services/p3FeedReader.h +++ b/plugins/FeedReader/services/p3FeedReader.h @@ -25,13 +25,10 @@ #include "retroshare/rsplugin.h" #include "plugins/rspqiservice.h" #include "interface/rsFeedReader.h" -#include "p3FeedReaderThread.h" class RsFeedReaderFeed; - -//TODO: get new id's -const uint8_t RS_PKT_TYPE_FEEDREADER_CONFIG = 0xf0; -const uint32_t CONFIG_TYPE_FEEDREADER = 0x0001; +class RsFeedReaderMsg; +class p3FeedReaderThread; class p3FeedReader : public RsPQIService, public RsFeedReader { @@ -66,16 +63,18 @@ public: virtual bool processFeed(const std::string &feedId); virtual bool setMessageRead(const std::string &feedId, const std::string &msgId, bool read); + virtual RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString); + /****************** p3Service STUFF ******************/ virtual int tick(); /****************** internal STUFF *******************/ bool getFeedToDownload(RsFeedReaderFeed &feed, const std::string &neededFeedId); void onDownloadSuccess(const std::string &feedId, const std::string &content, std::string &icon); - void onDownloadError(const std::string &feedId, p3FeedReaderThread::DownloadResult result, const std::string &errorString); - bool onProcessSuccess_filterMsg(const std::string &feedId, std::list &msgs); - void onProcessSuccess_addMsgs(const std::string &feedId, bool result, std::list &msgs, bool single); - void onProcessError(const std::string &feedId, p3FeedReaderThread::ProcessResult result, const std::string &errorString); + void onDownloadError(const std::string &feedId, RsFeedReaderErrorState result, const std::string &errorString); + void onProcessSuccess_filterMsg(const std::string &feedId, std::list &msgs); + void onProcessSuccess_addMsgs(const std::string &feedId, std::list &msgs, bool single); + void onProcessError(const std::string &feedId, RsFeedReaderErrorState result, const std::string &errorString); bool getFeedToProcess(RsFeedReaderFeed &feed, const std::string &neededFeedId); diff --git a/plugins/FeedReader/services/p3FeedReaderThread.cc b/plugins/FeedReader/services/p3FeedReaderThread.cc index 4ee228951..764491ea1 100644 --- a/plugins/FeedReader/services/p3FeedReaderThread.cc +++ b/plugins/FeedReader/services/p3FeedReaderThread.cc @@ -25,6 +25,7 @@ #include "util/CURLWrapper.h" #include "util/XMLWrapper.h" #include "util/HTMLWrapper.h" +#include "util/XPathWrapper.h" #include @@ -67,8 +68,8 @@ void p3FeedReaderThread::run() std::string icon; std::string errorString; - DownloadResult result = download(feed, content, icon, errorString); - if (result == DOWNLOAD_SUCCESS) { + RsFeedReaderErrorState result = download(feed, content, icon, errorString); + if (result == RS_FEED_ERRORSTATE_OK) { mFeedReader->onDownloadSuccess(feed.feedId, content, icon); } else { mFeedReader->onDownloadError(feed.feedId, result, errorString); @@ -84,37 +85,42 @@ void p3FeedReaderThread::run() std::string errorString; std::list::iterator it; - ProcessResult result = process(feed, msgs, errorString); - if (result == PROCESS_SUCCESS) { + RsFeedReaderErrorState result = process(feed, msgs, errorString); + if (result == RS_FEED_ERRORSTATE_OK) { /* first, filter the messages */ - bool result = mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs); + mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs); if (isRunning()) { - if (result) { - /* second, process the descriptions */ - for (it = msgs.begin(); it != msgs.end(); ) { - RsFeedReaderMsg *mi = *it; - processMsg(feed, mi); + /* second, process the descriptions */ + for (it = msgs.begin(); it != msgs.end(); ) { + RsFeedReaderMsg *mi = *it; + result = processMsg(feed, mi, errorString); + if (result != RS_FEED_ERRORSTATE_OK) { + break; + } - if (feed.preview) { - /* add every message */ - it = msgs.erase(it); + if (feed.preview) { + /* add every message */ + it = msgs.erase(it); - std::list msgSingle; - msgSingle.push_back(mi); - mFeedReader->onProcessSuccess_addMsgs(feed.feedId, result, msgSingle, true); + std::list msgSingle; + msgSingle.push_back(mi); + mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgSingle, true); - /* delete not accepted message */ - std::list::iterator it1; - for (it1 = msgSingle.begin(); it1 != msgSingle.end(); ++it1) { - delete (*it1); - } - } else { - ++it; + /* delete not accepted message */ + std::list::iterator it1; + for (it1 = msgSingle.begin(); it1 != msgSingle.end(); ++it1) { + delete (*it1); } + } else { + ++it; } } - /* third, add messages */ - mFeedReader->onProcessSuccess_addMsgs(feed.feedId, result, msgs, false); + if (result == RS_FEED_ERRORSTATE_OK) { + /* third, add messages */ + mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgs, false); + } else { + mFeedReader->onProcessError(feed.feedId, result, errorString); + } } } else { mFeedReader->onProcessError(feed.feedId, result, errorString); @@ -245,7 +251,7 @@ static bool getFavicon(CURLWrapper &CURL, const std::string &url, std::string &i return result; } -p3FeedReaderThread::DownloadResult p3FeedReaderThread::download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error) +RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReaderThread::download - feed " << feed.feedId << " (" << feed.name << ")" << std::endl; @@ -254,7 +260,7 @@ p3FeedReaderThread::DownloadResult p3FeedReaderThread::download(const RsFeedRead content.clear(); error.clear(); - DownloadResult result; + RsFeedReaderErrorState result; std::string proxy = getProxyForFeed(feed); CURLWrapper CURL(proxy); @@ -273,24 +279,24 @@ p3FeedReaderThread::DownloadResult p3FeedReaderThread::download(const RsFeedRead isContentType(contentType, "application/xml") || isContentType(contentType, "application/xhtml+xml")) { /* ok */ - result = DOWNLOAD_SUCCESS; + result = RS_FEED_ERRORSTATE_OK; } else { - result = DOWNLOAD_UNKNOWN_CONTENT_TYPE; + result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE; error = contentType; } } break; case 404: - result = DOWNLOAD_NOT_FOUND; + result = RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND; break; default: - result = DOWNLOAD_UNKOWN_RESPONSE_CODE; + result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE; rs_sprintf(error, "%ld", responseCode); } getFavicon(CURL, feed.url, icon); } else { - result = DOWNLOAD_ERROR; + result = RS_FEED_ERRORSTATE_DOWNLOAD_ERROR; error = curl_easy_strerror(code); } @@ -326,7 +332,7 @@ static xmlNodePtr getNextItem(FeedFormat feedFormat, xmlNodePtr channel, xmlNode item = item->next; } for (; item; item = item->next) { - if (item->type == XML_ELEMENT_NODE && xmlStrcasecmp(item->name, (const xmlChar*) "item") == 0) { + if (item->type == XML_ELEMENT_NODE && xmlStrEqual(item->name, BAD_CAST"item")) { break; } } @@ -789,29 +795,29 @@ static time_t parseISO8601Date(const std::string &pubDate) return result; } -p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReaderFeed &feed, std::list &entries, std::string &error) +RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed, std::list &entries, std::string &error) { #ifdef FEEDREADER_DEBUG std::cerr << "p3FeedReaderThread::process - feed " << feed.feedId << " (" << feed.name << ")" << std::endl; #endif - ProcessResult result = PROCESS_SUCCESS; + RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK; XMLWrapper xml; if (xml.readXML(feed.content.c_str())) { xmlNodePtr root = xml.getRootElement(); if (root) { FeedFormat feedFormat; - if (xmlStrcasecmp(root->name, (const xmlChar*) "rss") == 0) { + if (xmlStrEqual(root->name, BAD_CAST"rss")) { feedFormat = FORMAT_RSS; - } else if (xmlStrcasecmp (root->name, (const xmlChar*) "rdf") != 0) { + } else if (xmlStrEqual (root->name, BAD_CAST"rdf")) { feedFormat = FORMAT_RDF; } else { - result = PROCESS_UNKNOWN_FORMAT; + result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT; error = "Only RSS or RDF supported"; } - if (result == PROCESS_SUCCESS) { + if (result == RS_FEED_ERRORSTATE_OK) { xmlNodePtr channel = xml.findNode(root->children, "channel"); if (channel) { /* import header info */ @@ -900,16 +906,16 @@ p3FeedReaderThread::ProcessResult p3FeedReaderThread::process(const RsFeedReader entries.push_back(item); } } else { - result = PROCESS_UNKNOWN_FORMAT; + result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT; error = "Channel not found"; } } } else { - result = PROCESS_UNKNOWN_FORMAT; + result = RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT; error = "Can't read document"; } } else { - result = PROCESS_ERROR_INIT; + result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; } #ifdef FEEDREADER_DEBUG @@ -936,12 +942,15 @@ std::string p3FeedReaderThread::getProxyForFeed(const RsFeedReaderFeed &feed) return proxy; } -bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg) +RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString) { + long todo_fill_errorString; + if (!msg) { - return false; + return RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; } + RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK; std::string proxy = getProxyForFeed(feed); std::string url; @@ -953,14 +962,40 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs CURLWrapper CURL(proxy); CURLcode code = CURL.downloadText(msg->link, content); - if (code == CURLE_OK && CURL.responseCode() == 200 && isContentType(CURL.contentType(), "text/html")) { - /* ok */ - msg->description = content; + if (code == CURLE_OK) { + long responseCode = CURL.responseCode(); + + switch (responseCode) { + case 200: + { + std::string contentType = CURL.contentType(); + + if (isContentType(CURL.contentType(), "text/html")) { + /* ok */ + msg->description = content; + } else { + result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE; + errorString = contentType; + } + } + break; + case 404: + result = RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND; + break; + default: + result = RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE; + rs_sprintf(errorString, "%ld", responseCode); + } } else { + result = RS_FEED_ERRORSTATE_DOWNLOAD_ERROR; + errorString = curl_easy_strerror(code); + } + + if (result != RS_FEED_ERRORSTATE_OK) { #ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", responseCode = " << CURL.responseCode() << ", contentType = " << CURL.contentType() << std::endl; + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", error = " << errorString << std::endl; #endif - return false; + return result; } /* get effective url (moved location) */ @@ -970,89 +1005,355 @@ bool p3FeedReaderThread::processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMs /* check if string contains xml chars (very simple test) */ if (msg->description.find('<') == std::string::npos) { - return true; + return result; } - bool result = true; - /* process description */ long todo; // encoding HTMLWrapper html; if (html.readHTML(msg->description.c_str(), url.c_str())) { xmlNodePtr root = html.getRootElement(); if (root) { - /* process all children */ - std::list parents; - parents.push_back(root); + std::list nodesToDelete; - while (!parents.empty()) { + /* process all children */ + std::list nodes; + nodes.push_back(root); + + while (!nodes.empty()) { if (!isRunning()) { break; } - xmlNodePtr node = parents.front(); - parents.pop_front(); + xmlNodePtr node = nodes.front(); + nodes.pop_front(); - if (node->type == XML_ELEMENT_NODE) { - /* check for image */ - if (xmlStrcasecmp(node->name, (const xmlChar*) "img") == 0) { - bool removeImage = true; + switch (node->type) { + case XML_ELEMENT_NODE: + if (xmlStrEqual(node->name, BAD_CAST"img")) { + /* process images */ - if (feed.flag & RS_FEED_FLAG_EMBED_IMAGES) { - /* embed image */ - std::string src = html.getAttr(node, "src"); - if (!src.empty()) { - /* download image */ -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl; -#endif - std::vector data; - CURLWrapper CURL(proxy); - CURLcode code = CURL.downloadBinary(calculateLink(url, src), data); - if (code == CURLE_OK && CURL.responseCode() == 200) { - std::string contentType = CURL.contentType(); - if (isContentType(contentType, "image/")) { - std::string base64; - if (toBase64(data, base64)) { - std::string imageBase64; - rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str()); - if (html.setAttr(node, "src", imageBase64.c_str())) { - removeImage = false; - } - } - } - } - } - } - - if (removeImage) { + if ((feed.flag & RS_FEED_FLAG_EMBED_IMAGES) == 0) { /* remove image */ xmlUnlinkNode(node); - xmlFreeNode(node); + nodesToDelete.push_back(node); continue; } + } else if (xmlStrEqual(node->name, BAD_CAST"script")) { + /* remove script */ + xmlUnlinkNode(node); + nodesToDelete.push_back(node); + continue; } xmlNodePtr child; for (child = node->children; child; child = child->next) { - parents.push_back(child); + nodes.push_back(child); } + break; + + case XML_TEXT_NODE: + { + /* check for only space */ + std::string content; + if (html.getContent(node, content)) { + std::string newContent = content; + + /* trim left */ + std::string::size_type find = newContent.find_first_not_of(" \t\r\n"); + if (find != std::string::npos) { + newContent.erase(0, find); + + /* trim right */ + find = newContent.find_last_not_of(" \t\r\n"); + if (find != std::string::npos) { + newContent.erase(find + 1); + } + } else { + newContent.clear(); + } + + if (newContent.empty()) { + xmlUnlinkNode(node); + nodesToDelete.push_back(node); + } else { + if (content != newContent) { + html.setContent(node, newContent.c_str()); + } + } + } + } + break; + + case XML_COMMENT_NODE: +// xmlUnlinkNode(node); +// nodesToDelete.push_back(node); + break; + + case XML_ATTRIBUTE_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_DTD_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + break; } } - if (isRunning()) { - if (!html.saveHTML(msg->description)) { -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl; -#endif - result = false; - } - } else { -#ifdef FEEDREADER_DEBUG - std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl; -#endif - result = false; + std::list::iterator nodeIt; + for (nodeIt = nodesToDelete.begin(); nodeIt != nodesToDelete.end(); ++nodeIt) { + xmlFreeNode(*nodeIt); } + nodesToDelete.clear(); + + if (!feed.preview) { + result = processXPath(feed.xpathsToUse.ids, feed.xpathsToRemove.ids, html, errorString); + } + + if (result == RS_FEED_ERRORSTATE_OK) { + unsigned int xpathCount; + unsigned int xpathIndex; + XPathWrapper *xpath = html.createXPath(); + if (xpath) { + /* process images */ + if (xpath->compile("//img")) { + xpathCount = xpath->count(); + for (xpathIndex = 0; xpathIndex < xpathCount; ++xpathIndex) { + xmlNodePtr node = xpath->node(xpathIndex); + + if (node->type == XML_ELEMENT_NODE) { + bool removeImage = true; + + if (feed.flag & RS_FEED_FLAG_EMBED_IMAGES) { + /* embed image */ + std::string src = html.getAttr(node, "src"); + if (!src.empty()) { + /* download image */ +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl; +#endif + std::vector data; + CURLWrapper CURL(proxy); + CURLcode code = CURL.downloadBinary(calculateLink(url, src), data); + if (code == CURLE_OK && CURL.responseCode() == 200) { + std::string contentType = CURL.contentType(); + if (isContentType(contentType, "image/")) { + std::string base64; + if (toBase64(data, base64)) { + std::string imageBase64; + rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str()); + if (html.setAttr(node, "src", imageBase64.c_str())) { + removeImage = false; + } + } + } + } + } + } + + if (removeImage) { + /* remove image */ + xmlUnlinkNode(node); + nodesToDelete.push_back(node); + continue; + } + } + } + } else { + // unable to compile xpath expression + result = RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR; + } + delete(xpath); + xpath = NULL; + } else { + // unable to create xpath object + result = RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR; + std::cerr << "p3FeedReaderThread::process - feed " << feed.feedId << " (" << feed.name << "), unable to create xpath object" << std::endl; + } + } + + for (nodeIt = nodesToDelete.begin(); nodeIt != nodesToDelete.end(); ++nodeIt) { + xmlFreeNode(*nodeIt); + } + nodesToDelete.clear(); + + if (result == RS_FEED_ERRORSTATE_OK) { + if (isRunning()) { + if (!html.saveHTML(msg->description)) { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; + } + } + } + } else { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; } + } else { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; + } + + return result; +} + +RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, HTMLWrapper &html, std::string &errorString) +{ + long todo_fill_errorString; + + if (xpathsToUse.empty() && xpathsToRemove.empty()) { + return RS_FEED_ERRORSTATE_OK; + } + + XPathWrapper *xpath = html.createXPath(); + if (xpath == NULL) { + // unable to create xpath object + std::cerr << "p3FeedReaderThread::processXPath - unable to create xpath object" << std::endl; + return RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR; + } + + RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK; + + unsigned int xpathCount; + unsigned int xpathIndex; + std::list::const_iterator xpathIt; + + if (!xpathsToUse.empty()) { + HTMLWrapper htmlNew; + if (htmlNew.createHTML()) { + xmlNodePtr body = htmlNew.getBody(); + if (body) { + /* process use list */ + for (xpathIt = xpathsToUse.begin(); xpathIt != xpathsToUse.end(); ++xpathIt) { + if (xpath->compile(xpathIt->c_str())) { + xpathCount = xpath->count(); + if (xpathCount) { + for (xpathIndex = 0; xpathIndex < xpathCount; ++xpathIndex) { + xmlNodePtr node = xpath->node(xpathIndex); + xmlUnlinkNode(node); + xmlAddChild(body, node); + } + } else { + result = RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT; + errorString = *xpathIt; + break; + } + } else { + // unable to process xpath expression +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processXPath - unable to process xpath expression" << std::endl; +#endif + errorString = *xpathIt; + result = RS_FEED_ERRORSTATE_PROCESS_XPATH_WRONG_EXPRESSION; + } + } + } else { + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; + } + } else { + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; + } + + if (result == RS_FEED_ERRORSTATE_OK) { + html = htmlNew; + } + } + + if (result == RS_FEED_ERRORSTATE_OK) { + std::list nodesToDelete; + + /* process remove list */ + for (xpathIt = xpathsToRemove.begin(); xpathIt != xpathsToRemove.end(); ++xpathIt) { + if (xpath->compile(xpathIt->c_str())) { + xpathCount = xpath->count(); + if (xpathCount) { + for (xpathIndex = 0; xpathIndex < xpathCount; ++xpathIndex) { + xmlNodePtr node = xpath->node(xpathIndex); + + xmlUnlinkNode(node); + nodesToDelete.push_back(node); + } + } else { + result = RS_FEED_ERRORSTATE_PROCESS_XPATH_NO_RESULT; + errorString = *xpathIt; + break; + } + } else { + // unable to process xpath expression +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processXPath - unable to process xpath expression" << std::endl; +#endif + errorString = *xpathIt; + return RS_FEED_ERRORSTATE_PROCESS_XPATH_WRONG_EXPRESSION; + } + } + + std::list::iterator nodeIt; + for (nodeIt = nodesToDelete.begin(); nodeIt != nodesToDelete.end(); ++nodeIt) { + xmlFreeNode(*nodeIt); + } + nodesToDelete.clear(); + } + + return result; +} + +RsFeedReaderErrorState p3FeedReaderThread::processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString) +{ + if (xpathsToUse.empty() && xpathsToRemove.empty()) { + return RS_FEED_ERRORSTATE_OK; + } + + RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK; + + long todo_fill_errorString; + + /* process description */ + long todo; // encoding + HTMLWrapper html; + if (html.readHTML(description.c_str(), "")) { + xmlNodePtr root = html.getRootElement(); + if (root) { + result = processXPath(xpathsToUse, xpathsToRemove, html, errorString); + + if (result == RS_FEED_ERRORSTATE_OK) { + if (!html.saveHTML(description)) { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processXPath - cannot dump html" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR; + } + } + } else { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processXPath - no root element" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; + } + } else { +#ifdef FEEDREADER_DEBUG + std::cerr << "p3FeedReaderThread::processXPath - cannot read html" << std::endl; +#endif + result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR; } return result; diff --git a/plugins/FeedReader/services/p3FeedReaderThread.h b/plugins/FeedReader/services/p3FeedReaderThread.h index 5da053fd0..9e3e926b9 100644 --- a/plugins/FeedReader/services/p3FeedReaderThread.h +++ b/plugins/FeedReader/services/p3FeedReaderThread.h @@ -22,12 +22,16 @@ #ifndef P3_FEEDREADERTHREAD #define P3_FEEDREADERTHREAD +#include "interface/rsFeedReader.h" + #include "util/rsthreads.h" #include class p3FeedReader; class RsFeedReaderFeed; class RsFeedReaderMsg; +class HTMLWrapper; +class RsFeedReaderXPath; class p3FeedReaderThread : public RsThread { @@ -37,20 +41,6 @@ public: DOWNLOAD, PROCESS }; - enum DownloadResult - { - DOWNLOAD_SUCCESS, - DOWNLOAD_ERROR, - DOWNLOAD_UNKNOWN_CONTENT_TYPE, - DOWNLOAD_NOT_FOUND, - DOWNLOAD_UNKOWN_RESPONSE_CODE - }; - enum ProcessResult - { - PROCESS_SUCCESS, - PROCESS_ERROR_INIT, - PROCESS_UNKNOWN_FORMAT - }; public: p3FeedReaderThread(p3FeedReader *feedReader, Type type, const std::string &feedId); @@ -58,14 +48,17 @@ public: std::string getFeedId() { return mFeedId; } + static RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, std::string &description, std::string &errorString); + static RsFeedReaderErrorState processXPath(const std::list &xpathsToUse, const std::list &xpathsToRemove, HTMLWrapper &html, std::string &errorString); + private: virtual void run(); - DownloadResult download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error); - ProcessResult process(const RsFeedReaderFeed &feed, std::list &entries, std::string &error); + RsFeedReaderErrorState download(const RsFeedReaderFeed &feed, std::string &content, std::string &icon, std::string &error); + RsFeedReaderErrorState process(const RsFeedReaderFeed &feed, std::list &entries, std::string &error); std::string getProxyForFeed(const RsFeedReaderFeed &feed); - bool processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg); + RsFeedReaderErrorState processMsg(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString); p3FeedReader *mFeedReader; Type mType; diff --git a/plugins/FeedReader/services/rsFeedReaderItems.cc b/plugins/FeedReader/services/rsFeedReaderItems.cc index 2183ffcdd..1d5f60cdd 100644 --- a/plugins/FeedReader/services/rsFeedReaderItems.cc +++ b/plugins/FeedReader/services/rsFeedReaderItems.cc @@ -25,7 +25,9 @@ /*************************************************************************/ -RsFeedReaderFeed::RsFeedReaderFeed() : RsItem(RS_PKT_VERSION1, RS_PKT_CLASS_CONFIG, RS_PKT_TYPE_FEEDREADER_CONFIG, RS_PKT_SUBTYPE_FEEDREADER_FEED) +RsFeedReaderFeed::RsFeedReaderFeed() + : RsItem(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_PLUGIN_FEEDREADER, RS_PKT_SUBTYPE_FEEDREADER_FEED), + xpathsToUse(TLV_TYPE_STRINGSET), xpathsToRemove(TLV_TYPE_STRINGSET) { clear(); } @@ -49,6 +51,8 @@ void RsFeedReaderFeed::clear() icon.clear(); errorState = RS_FEED_ERRORSTATE_OK; errorString.clear(); + xpathsToUse.ids.clear(); + xpathsToRemove.ids.clear(); preview = false; workstate = WAITING; @@ -63,6 +67,7 @@ std::ostream &RsFeedReaderFeed::print(std::ostream &out, uint16_t /*indent*/) uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item) { uint32_t s = 8; /* header */ + s += 2; /* version */ s += GetTlvStringSize(item->feedId); s += GetTlvStringSize(item->parentId); s += GetTlvStringSize(item->url); @@ -80,6 +85,8 @@ uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item) s += GetTlvStringSize(item->forumId); s += sizeof(uint32_t); /* errorstate */ s += GetTlvStringSize(item->errorString); + s += item->xpathsToUse.TlvSize(); + s += item->xpathsToRemove.TlvSize(); return s; } @@ -103,6 +110,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u offset += 8; /* add values */ + ok &= setRawUInt16(data, tlvsize, &offset, 0); /* version */ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->feedId); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->parentId); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LINK, item->url); @@ -120,6 +128,8 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->forumId); ok &= setRawUInt32(data, tlvsize, &offset, item->errorState); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->errorString); + ok &= item->xpathsToUse.SetTlv(data, tlvsize, &offset); + ok &= item->xpathsToRemove.SetTlv(data, tlvsize, &offset); if (offset != tlvsize) { @@ -138,9 +148,8 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t * uint32_t offset = 0; - if ((RS_PKT_VERSION1 != getRsItemVersion(rstype)) || - (RS_PKT_CLASS_CONFIG != getRsItemClass(rstype)) || - (RS_PKT_TYPE_FEEDREADER_CONFIG != getRsItemType(rstype)) || + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || + (RS_SERVICE_TYPE_PLUGIN_FEEDREADER != getRsItemService(rstype)) || (RS_PKT_SUBTYPE_FEEDREADER_FEED != getRsItemSubType(rstype))) { return NULL; /* wrong type */ @@ -162,6 +171,8 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t * offset += 8; /* get values */ + uint16_t version = 0; + ok &= getRawUInt16(data, rssize, &offset, &version); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_GENID, item->feedId); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->parentId); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_LINK, item->url); @@ -181,6 +192,8 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t * ok &= getRawUInt32(data, rssize, &offset, &errorState); item->errorState = (RsFeedReaderErrorState) errorState; ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->errorString); + ok &= item->xpathsToUse.GetTlv(data, rssize, &offset); + ok &= item->xpathsToRemove.GetTlv(data, rssize, &offset); if (offset != rssize) { @@ -200,7 +213,7 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t * /*************************************************************************/ -RsFeedReaderMsg::RsFeedReaderMsg() : RsItem(RS_PKT_VERSION1, RS_PKT_CLASS_CONFIG, RS_PKT_TYPE_FEEDREADER_CONFIG, RS_PKT_SUBTYPE_FEEDREADER_MSG) +RsFeedReaderMsg::RsFeedReaderMsg() : RsItem(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_PLUGIN_FEEDREADER, RS_PKT_SUBTYPE_FEEDREADER_MSG) { clear(); } @@ -225,6 +238,7 @@ std::ostream &RsFeedReaderMsg::print(std::ostream &out, uint16_t /*indent*/) uint32_t RsFeedReaderSerialiser::sizeMsg(RsFeedReaderMsg *item) { uint32_t s = 8; /* header */ + s += 2; /* version */ s += GetTlvStringSize(item->msgId); s += GetTlvStringSize(item->feedId); s += GetTlvStringSize(item->title); @@ -256,6 +270,7 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin offset += 8; /* add values */ + ok &= setRawUInt16(data, tlvsize, &offset, 0); /* version */ ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->feedId); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title); @@ -282,9 +297,8 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk uint32_t offset = 0; - if ((RS_PKT_VERSION1 != getRsItemVersion(rstype)) || - (RS_PKT_CLASS_CONFIG != getRsItemClass(rstype)) || - (RS_PKT_TYPE_FEEDREADER_CONFIG != getRsItemType(rstype)) || + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || + (RS_SERVICE_TYPE_PLUGIN_FEEDREADER != getRsItemService(rstype)) || (RS_PKT_SUBTYPE_FEEDREADER_MSG != getRsItemSubType(rstype))) { return NULL; /* wrong type */ @@ -306,6 +320,8 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk offset += 8; /* get values */ + uint16_t version = 0; + ok &= getRawUInt16(data, rssize, &offset, &version); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_GENID, item->msgId); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->feedId); ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_NAME, item->title); @@ -372,9 +388,8 @@ RsItem *RsFeedReaderSerialiser::deserialise(void *data, uint32_t *pktsize) /* get the type and size */ uint32_t rstype = getRsItemId(data); - if ((RS_PKT_VERSION1 != getRsItemVersion(rstype)) || - (RS_PKT_CLASS_CONFIG != getRsItemClass(rstype)) || - (RS_PKT_TYPE_FEEDREADER_CONFIG != getRsItemType(rstype))) + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || + (RS_SERVICE_TYPE_PLUGIN_FEEDREADER != getRsItemService(rstype))) { return NULL; /* wrong type */ } diff --git a/plugins/FeedReader/services/rsFeedReaderItems.h b/plugins/FeedReader/services/rsFeedReaderItems.h index 3a2e720c3..847034456 100644 --- a/plugins/FeedReader/services/rsFeedReaderItems.h +++ b/plugins/FeedReader/services/rsFeedReaderItems.h @@ -22,11 +22,14 @@ #ifndef RS_FEEDREADER_ITEMS_H #define RS_FEEDREADER_ITEMS_H +#include "serialiser/rsserviceids.h" #include "serialiser/rsserial.h" #include "serialiser/rstlvtypes.h" #include "p3FeedReader.h" +const uint32_t CONFIG_TYPE_FEEDREADER = 0x0001; // is this correct? + const uint8_t RS_PKT_SUBTYPE_FEEDREADER_FEED = 0x02; const uint8_t RS_PKT_SUBTYPE_FEEDREADER_MSG = 0x03; @@ -80,12 +83,15 @@ public: RsFeedReaderErrorState errorState; std::string errorString; + RsTlvStringSet xpathsToUse; + RsTlvStringSet xpathsToRemove; + /* Not Serialised */ bool preview; WorkState workstate; std::string content; - std::map mMsgs; + std::map msgs; }; #define RS_FEEDMSG_FLAG_DELETED 1 @@ -114,7 +120,7 @@ public: class RsFeedReaderSerialiser: public RsSerialType { public: - RsFeedReaderSerialiser() : RsSerialType(RS_PKT_VERSION1, RS_PKT_CLASS_CONFIG, RS_PKT_TYPE_FEEDREADER_CONFIG) {} + RsFeedReaderSerialiser() : RsSerialType(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_PLUGIN_FEEDREADER) {} virtual ~RsFeedReaderSerialiser() {} virtual uint32_t size(RsItem *item); diff --git a/plugins/FeedReader/util/HTMLWrapper.cpp b/plugins/FeedReader/util/HTMLWrapper.cpp index 2722af72e..12c7c727a 100644 --- a/plugins/FeedReader/util/HTMLWrapper.cpp +++ b/plugins/FeedReader/util/HTMLWrapper.cpp @@ -32,7 +32,7 @@ bool HTMLWrapper::readHTML(const char *html, const char *url) { cleanup(); - mDocument = htmlReadMemory(html, strlen(html), url, "", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING/* | HTML_PARSE_COMPACT*/); + mDocument = htmlReadMemory(html, strlen(html), url, "", HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_COMPACT | HTML_PARSE_NONET | HTML_PARSE_NOBLANKS); if (mDocument) { return true; } @@ -58,3 +58,18 @@ bool HTMLWrapper::saveHTML(std::string &html) return false; } + +bool HTMLWrapper::createHTML() +{ + /* easy way */ + return readHTML("", ""); +} + +xmlNodePtr HTMLWrapper::getBody() +{ + xmlNodePtr root = getRootElement(); + if (!root) { + return NULL; + } + return findNode(root->children, "body", false); +} diff --git a/plugins/FeedReader/util/HTMLWrapper.h b/plugins/FeedReader/util/HTMLWrapper.h index e93552da8..a19b8313f 100644 --- a/plugins/FeedReader/util/HTMLWrapper.h +++ b/plugins/FeedReader/util/HTMLWrapper.h @@ -31,6 +31,10 @@ public: bool readHTML(const char *html, const char *url); bool saveHTML(std::string &html); + + bool createHTML(); + + xmlNodePtr getBody(); }; #endif diff --git a/plugins/FeedReader/util/XMLWrapper.cpp b/plugins/FeedReader/util/XMLWrapper.cpp index ddb5a02fc..c1a4b2143 100644 --- a/plugins/FeedReader/util/XMLWrapper.cpp +++ b/plugins/FeedReader/util/XMLWrapper.cpp @@ -23,6 +23,7 @@ #include #include "XMLWrapper.h" +#include "XPathWrapper.h" XMLWrapper::XMLWrapper() { @@ -41,6 +42,18 @@ XMLWrapper::~XMLWrapper() xmlCharEncCloseFunc(mCharEncodingHandler); } +XMLWrapper &XMLWrapper::operator=(const XMLWrapper &xml) +{ + cleanup(); + + const xmlDocPtr document = xml.getDocument(); + if (document) { + mDocument = xmlCopyDoc(document, 1); + } + + return *this; +} + void XMLWrapper::cleanup() { if (mDocument) { @@ -73,7 +86,7 @@ bool XMLWrapper::convertFromString(const char *text, xmlChar *&xmlText) xmlBufferPtr in = xmlBufferCreateStatic((void*) text, strlen(text)); xmlBufferPtr out = xmlBufferCreate(); - int ret = xmlCharEncOutFunc(mCharEncodingHandler, out, in); + int ret = xmlCharEncInFunc(mCharEncodingHandler, out, in); if (ret >= 0) { result = true; xmlText = xmlBufferDetach(out); @@ -85,7 +98,12 @@ bool XMLWrapper::convertFromString(const char *text, xmlChar *&xmlText) return result; } -xmlNodePtr XMLWrapper::getRootElement() +xmlDocPtr XMLWrapper::getDocument() const +{ + return mDocument; +} + +xmlNodePtr XMLWrapper::getRootElement() const { if (mDocument) { return xmlDocGetRootElement(mDocument); @@ -98,7 +116,7 @@ bool XMLWrapper::readXML(const char *xml) { cleanup(); - mDocument = xmlReadDoc((const xmlChar*) xml, "", NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING/* | XML_PARSE_COMPACT | XML_PARSE_NOCDATA*/); + mDocument = xmlReadDoc(BAD_CAST xml, "", NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_COMPACT | XML_PARSE_NOENT | XML_PARSE_NOCDATA); if (mDocument) { return true; } @@ -114,7 +132,7 @@ bool XMLWrapper::getContent(xmlNodePtr node, std::string &content) return false; } - xmlChar *xmlContent = xmlNodeListGetString(mDocument, node, 1); + xmlChar *xmlContent = xmlNodeGetContent(node); if (!xmlContent) { return true; } @@ -125,6 +143,23 @@ bool XMLWrapper::getContent(xmlNodePtr node, std::string &content) return result; } +bool XMLWrapper::setContent(xmlNodePtr node, const char *content) +{ + if (!node) { + return false; + } + + xmlChar *xmlContent; + if (!convertFromString(content, xmlContent)) { + return false; + } + + xmlNodeSetContent(node, xmlContent); + xmlFree(xmlContent); + + return true; +} + std::string XMLWrapper::nodeName(xmlNodePtr node) { std::string name; @@ -150,7 +185,7 @@ std::string XMLWrapper::attrName(xmlAttrPtr attr) xmlNodePtr XMLWrapper::findNode(xmlNodePtr node, const char *name, bool children) { if (node->name) { - if (xmlStrcasecmp(node->name, (xmlChar*) name) == 0) { + if (xmlStrEqual(node->name, BAD_CAST name)) { return node; } } @@ -218,7 +253,7 @@ std::string XMLWrapper::getAttr(xmlNodePtr node, const char *name) std::string value; - xmlChar *xmlValue = xmlGetProp(node, (const xmlChar*) name); + xmlChar *xmlValue = xmlGetProp(node, BAD_CAST name); if (xmlValue) { convertToString(xmlValue, value); xmlFree(xmlValue); @@ -238,8 +273,17 @@ bool XMLWrapper::setAttr(xmlNodePtr node, const char *name, const char *value) return false; } - xmlAttrPtr xmlAttr = xmlSetProp (node, (const xmlChar*) name, xmlValue); + xmlAttrPtr xmlAttr = xmlSetProp (node, BAD_CAST name, xmlValue); xmlFree(xmlValue); return xmlAttr != NULL; } + +XPathWrapper *XMLWrapper::createXPath() +{ + if (mDocument) { + return new XPathWrapper(*this); + } + + return NULL; +} diff --git a/plugins/FeedReader/util/XMLWrapper.h b/plugins/FeedReader/util/XMLWrapper.h index 8a896d44c..6b94ea796 100644 --- a/plugins/FeedReader/util/XMLWrapper.h +++ b/plugins/FeedReader/util/XMLWrapper.h @@ -25,16 +25,23 @@ #include #include +class XPathWrapper; + class XMLWrapper { public: XMLWrapper(); ~XMLWrapper(); + XMLWrapper &operator=(const XMLWrapper &xml); + void cleanup(); bool readXML(const char *xml); + xmlDocPtr getDocument() const; + xmlNodePtr getRootElement() const; + std::string nodeName(xmlNodePtr node); std::string attrName(xmlAttrPtr attr); @@ -42,19 +49,20 @@ public: bool getChildText(xmlNodePtr node, const char *childName, std::string &text); bool getContent(xmlNodePtr node, std::string &content); + bool setContent(xmlNodePtr node, const char *content); std::string getAttr(xmlNodePtr node, xmlAttrPtr attr); std::string getAttr(xmlNodePtr node, const char *name); bool setAttr(xmlNodePtr node, const char *name, const char *value); - xmlNodePtr getRootElement(); + XPathWrapper *createXPath(); + + bool convertToString(const xmlChar *xmlText, std::string &text); + bool convertFromString(const char *text, xmlChar *&xmlText); protected: xmlDocPtr mDocument; xmlCharEncodingHandlerPtr mCharEncodingHandler; - - bool convertToString(const xmlChar *xmlText, std::string &text); - bool convertFromString(const char *text, xmlChar *&xmlText); }; #endif diff --git a/plugins/FeedReader/util/XPathWrapper.cpp b/plugins/FeedReader/util/XPathWrapper.cpp new file mode 100644 index 000000000..103790493 --- /dev/null +++ b/plugins/FeedReader/util/XPathWrapper.cpp @@ -0,0 +1,102 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include "XPathWrapper.h" +#include "XMLWrapper.h" + +XPathWrapper::XPathWrapper(XMLWrapper &xmlWrapper) : mXMLWrapper(xmlWrapper) +{ + mContext = NULL; + mResult = NULL; +} + +XPathWrapper::~XPathWrapper() +{ + cleanup(); +} + +void XPathWrapper::cleanup() +{ + if (mResult) { + xmlXPathFreeObject(mResult); + mResult = NULL; + } + if (mContext) { + xmlXPathFreeContext(mContext); + mContext = NULL; + } +} + +bool XPathWrapper::compile(const char *expression) +{ + cleanup(); + + xmlDocPtr document = mXMLWrapper.getDocument(); + if (!document) { + return false; + } + + mContext = xmlXPathNewContext(document); + if (!mContext) { + cleanup(); + return false; + } + + xmlChar *xmlExpression = NULL; + if (!mXMLWrapper.convertFromString(expression, xmlExpression)) { + cleanup(); + return false; + } + mResult = xmlXPathEvalExpression(xmlExpression, mContext); + xmlFree(xmlExpression); + + return true; +} + +unsigned int XPathWrapper::count() +{ + if (!mResult) { + return 0; + } + + if (xmlXPathNodeSetIsEmpty(mResult->nodesetval)) { + return 0; + } + + return mResult->nodesetval->nodeNr; +} + +xmlNodePtr XPathWrapper::node(unsigned int index) +{ + if (!mResult) { + return NULL; + } + + if (xmlXPathNodeSetIsEmpty(mResult->nodesetval)) { + return NULL; + } + + if (index >= (unsigned int) mResult->nodesetval->nodeNr) { + return NULL; + } + + return mResult->nodesetval->nodeTab[index]; +} diff --git a/plugins/FeedReader/util/XPathWrapper.h b/plugins/FeedReader/util/XPathWrapper.h new file mode 100644 index 000000000..1c30df1aa --- /dev/null +++ b/plugins/FeedReader/util/XPathWrapper.h @@ -0,0 +1,51 @@ +/**************************************************************** + * RetroShare GUI is distributed under the following license: + * + * Copyright (C) 2012 by Thunder + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#ifndef XPATHWRAPPER +#define XPATHWRAPPER + +#include + +class XMLWrapper; + +class XPathWrapper +{ + friend class XMLWrapper; + +public: + ~XPathWrapper(); + + void cleanup(); + + bool compile(const char *expression); + + unsigned int count(); + xmlNodePtr node(unsigned int index); + +protected: + XPathWrapper(XMLWrapper &xmlWrapper); + + XMLWrapper &mXMLWrapper; + xmlXPathContextPtr mContext; + xmlXPathObjectPtr mResult; +}; + +#endif