FeedReader: Added processing of enclosure in RSS feed

This commit is contained in:
thunder2 2023-05-09 07:49:34 +02:00
parent f9ca6cd3e1
commit ad9d566767
15 changed files with 406 additions and 108 deletions

View File

@ -100,7 +100,7 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
ui->postedOnlyImageCheckBox->setEnabled(false); ui->postedOnlyImageCheckBox->setEnabled(false);
ui->postedOnlyImageCheckBox->setChecked(false); ui->postedOnlyImageCheckBox->setChecked(false);
ui->postedShinkImageCheckBox->setEnabled(false); ui->postedShinkImageCheckBox->setEnabled(false);
ui->postedShinkImageCheckBox->setChecked(false); ui->postedShinkImageCheckBox->setChecked(true);
ui->useAuthenticationCheckBox->setChecked(false); ui->useAuthenticationCheckBox->setChecked(false);
ui->useStandardStorageTimeCheckBox->setChecked(true); ui->useStandardStorageTimeCheckBox->setChecked(true);
ui->useStandardUpdateInterval->setChecked(true); ui->useStandardUpdateInterval->setChecked(true);
@ -199,11 +199,9 @@ void AddFeedDialog::postedFirstImageToggled()
{ {
bool checked = ui->postedFirstImageCheckBox->isChecked(); bool checked = ui->postedFirstImageCheckBox->isChecked();
ui->postedOnlyImageCheckBox->setEnabled(checked); ui->postedOnlyImageCheckBox->setEnabled(checked);
ui->postedShinkImageCheckBox->setEnabled(checked);
if (!checked) { if (!checked) {
ui->postedOnlyImageCheckBox->setChecked(false); ui->postedOnlyImageCheckBox->setChecked(false);
ui->postedShinkImageCheckBox->setChecked(false);
} }
} }

View File

@ -23,6 +23,7 @@
#include <QInputDialog> #include <QInputDialog>
#include <QPainter> #include <QPainter>
#include <QMessageBox> #include <QMessageBox>
#include <QBuffer>
#include "FeedReaderDialog.h" #include "FeedReaderDialog.h"
#include "FeedReaderMessageWidget.h" #include "FeedReaderMessageWidget.h"
@ -36,6 +37,7 @@
#include "gui/notifyqt.h" #include "gui/notifyqt.h"
#include "FeedReaderUserNotify.h" #include "FeedReaderUserNotify.h"
#include "gui/Posted/PostedCreatePostDialog.h" #include "gui/Posted/PostedCreatePostDialog.h"
#include "util/imageutil.h"
#include "interface/rsFeedReader.h" #include "interface/rsFeedReader.h"
#include "retroshare/rsiface.h" #include "retroshare/rsiface.h"
@ -56,6 +58,8 @@
#define ROLE_FEED_ERROR Qt::UserRole + 9 #define ROLE_FEED_ERROR Qt::UserRole + 9
#define ROLE_FEED_DEACTIVATED Qt::UserRole + 10 #define ROLE_FEED_DEACTIVATED Qt::UserRole + 10
const int MAXMESSAGESIZE = RsSerialiser::MAX_SERIAL_SIZE - 70000;
FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent) FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent)
: MainPage(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::FeedReaderDialog) : MainPage(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::FeedReaderDialog)
{ {
@ -67,7 +71,7 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *n
mMessageWidget = NULL; mMessageWidget = NULL;
connect(mNotify, &FeedReaderNotify::feedChanged, this, &FeedReaderDialog::feedChanged, Qt::QueuedConnection); connect(mNotify, &FeedReaderNotify::feedChanged, this, &FeedReaderDialog::feedChanged, Qt::QueuedConnection);
connect(mNotify, &FeedReaderNotify::shrinkImage, this, &FeedReaderDialog::shrinkImage, Qt::QueuedConnection); connect(mNotify, &FeedReaderNotify::optimizeImage, this, &FeedReaderDialog::optimizeImage, Qt::QueuedConnection);
connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged()));
@ -606,34 +610,52 @@ void FeedReaderDialog::feedChanged(uint32_t feedId, int type)
calculateFeedItems(); calculateFeedItems();
} }
void FeedReaderDialog::shrinkImage() void FeedReaderDialog::optimizeImage()
{ {
while (true) { while (true) {
FeedReaderShrinkImageTask *shrinkImageTask = mFeedReader->getShrinkImageTask(); FeedReaderOptimizeImageTask *optimizeImageTask = mFeedReader->getOptimizeImageTask();
if (!shrinkImageTask) { if (!optimizeImageTask) {
return; return;
} }
switch (shrinkImageTask->mType) { optimizeImageTask->mResult = false;
case FeedReaderShrinkImageTask::POSTED:
{ QImage image;
QImage image; if (image.loadFromData(optimizeImageTask->mImage.data(), optimizeImageTask->mImage.size())) {
if (image.loadFromData(shrinkImageTask->mImage.data(), shrinkImageTask->mImage.size())) { QByteArray optimizedImageData;
QByteArray imageBytes; std::string optimizedImageMimeType;
QImage imageOpt; QImage imageOptDummy;
if (PostedCreatePostDialog::optimizeImage(image, imageBytes, imageOpt)) {
shrinkImageTask->mImageResult.assign(imageBytes.begin(), imageBytes.end()); switch (optimizeImageTask->mType) {
shrinkImageTask->mResult = true; case FeedReaderOptimizeImageTask::POSTED:
if (PostedCreatePostDialog::optimizeImage(image, optimizedImageData, imageOptDummy)) {
optimizedImageMimeType = "image/jpeg";
optimizeImageTask->mResult = true;
}
break;
case FeedReaderOptimizeImageTask::SIZE:
if (ImageUtil::optimizeSizeBytes(optimizedImageData, image, imageOptDummy, "JPG", 0, MAXMESSAGESIZE)) {
optimizedImageMimeType = "image/jpg";
optimizeImageTask->mResult = true;
}
break;
}
if (optimizeImageTask->mResult) {
if (optimizedImageData.size() < (ssize_t) optimizeImageTask->mImage.size()) {
/* use optimized image */
optimizeImageTask->mImageResult.assign(optimizedImageData.begin(), optimizedImageData.end());
optimizeImageTask->mMimeTypeResult = optimizedImageMimeType;
} else {
/* use original image */
optimizeImageTask->mImageResult = optimizeImageTask->mImage;
optimizeImageTask->mMimeTypeResult = optimizeImageTask->mMimeType;
} }
} }
break;
}
default:
shrinkImageTask->mResult = false;
} }
mFeedReader->setShrinkImageTaskResult(shrinkImageTask); mFeedReader->setOptimizeImageTaskResult(optimizeImageTask);
} }
} }

View File

@ -69,7 +69,7 @@ private slots:
/* FeedReaderNotify */ /* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type); void feedChanged(uint32_t feedId, int type);
void shrinkImage(); void optimizeImage();
private: private:
uint32_t currentFeedId(); uint32_t currentFeedId();

View File

@ -25,6 +25,7 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QTimer> #include <QTimer>
#include <QPainter> #include <QPainter>
#include <QMimeData>
#include "FeedReaderMessageWidget.h" #include "FeedReaderMessageWidget.h"
#include "ui_FeedReaderMessageWidget.h" #include "ui_FeedReaderMessageWidget.h"
@ -38,6 +39,8 @@
#include "util/QtVersion.h" #include "util/QtVersion.h"
#include "gui/Posted/PostedCreatePostDialog.h" #include "gui/Posted/PostedCreatePostDialog.h"
#include "gui/gxsforums/CreateGxsForumMsg.h" #include "gui/gxsforums/CreateGxsForumMsg.h"
#include "gui/common/FilesDefs.h"
#include "util/imageutil.h"
#include "retroshare/rsiface.h" #include "retroshare/rsiface.h"
#include "retroshare/rsgxsforums.h" #include "retroshare/rsgxsforums.h"
@ -87,6 +90,12 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
connect(ui->msgRemoveButton, SIGNAL(clicked()), this, SLOT(removeMsg())); connect(ui->msgRemoveButton, SIGNAL(clicked()), this, SLOT(removeMsg()));
connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed())); connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed()));
/* Set initial size the splitter */
QList<int> sizes;
sizes << 250 << width(); // Qt calculates the right sizes
ui->pictureSplitter->setSizes(sizes);
ui->pictureSplitter->hide();
// create timer for navigation // create timer for navigation
mTimer = new QTimer(this); mTimer = new QTimer(this);
mTimer->setInterval(300); mTimer->setInterval(300);
@ -119,6 +128,10 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author")); ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author"));
ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE); ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE);
/* add image actions */
ui->attachmentLabel->addContextMenuAction(ui->actionAttachmentCopyLinkLocation);
connect(ui->actionAttachmentCopyLinkLocation, &QAction::triggered, this, &FeedReaderMessageWidget::attachmentCopyLinkLocation);
/* load settings */ /* load settings */
processSettings(true); processSettings(true);
@ -179,6 +192,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter // state of splitter
ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray()); ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray());
ui->pictureSplitter->restoreState(Settings->value("pictureSplitter").toByteArray());
} else { } else {
// save settings // save settings
@ -187,6 +201,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter // state of splitter
Settings->setValue("msgSplitter", ui->msgSplitter->saveState()); Settings->setValue("msgSplitter", ui->msgSplitter->saveState());
Settings->setValue("pictureSplitter", ui->pictureSplitter->saveState());
} }
Settings->endGroup(); Settings->endGroup();
@ -599,6 +614,21 @@ void FeedReaderMessageWidget::msgItemChanged()
mTimer->start(); mTimer->start();
} }
void FeedReaderMessageWidget::clearMessage()
{
ui->msgTitle->clear();
// ui->msgLink->clear();
ui->msgText->clear();
ui->msgTextSplitter->clear();
ui->attachmentLabel->clear();
ui->linkButton->setEnabled(false);
ui->msgText->show();
ui->pictureSplitter->hide();
ui->actionAttachmentCopyLinkLocation->setData(QVariant());
ui->actionAttachmentCopyLinkLocation->setEnabled(false);
}
void FeedReaderMessageWidget::updateCurrentMessage() void FeedReaderMessageWidget::updateCurrentMessage()
{ {
mTimer->stop(); mTimer->stop();
@ -608,10 +638,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
std::string msgId = currentMsgId(); std::string msgId = currentMsgId();
if (mFeedId == 0 || msgId.empty()) { if (mFeedId == 0 || msgId.empty()) {
ui->msgTitle->clear(); clearMessage();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
ui->msgReadButton->setEnabled(false); ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false); ui->msgUnreadButton->setEnabled(false);
@ -622,10 +649,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
QTreeWidgetItem *item = ui->msgTreeWidget->currentItem(); QTreeWidgetItem *item = ui->msgTreeWidget->currentItem();
if (!item) { if (!item) {
/* there is something wrong */ /* there is something wrong */
ui->msgTitle->clear(); clearMessage();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
ui->msgReadButton->setEnabled(false); ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false); ui->msgUnreadButton->setEnabled(false);
@ -640,10 +664,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
/* get msg */ /* get msg */
FeedMsgInfo msgInfo; FeedMsgInfo msgInfo;
if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) { if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
ui->msgTitle->clear(); clearMessage();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
return; return;
} }
@ -663,9 +684,41 @@ void FeedReaderMessageWidget::updateCurrentMessage()
setMsgAsReadUnread(row, setToReadOnActive); setMsgAsReadUnread(row, setToReadOnActive);
} }
ui->actionAttachmentCopyLinkLocation->setData(QVariant());
ui->actionAttachmentCopyLinkLocation->setEnabled(false);
if (!msgInfo.attachment.empty()) {
QByteArray imageData((char*) msgInfo.attachment.data(), msgInfo.attachment.size());
QPixmap pixmap;
if (pixmap.loadFromData(imageData)) {
ui->attachmentLabel->setPixmap(pixmap);
ui->pictureSplitter->show();
ui->msgText->hide();
} else {
ui->pictureSplitter->hide();
ui->msgText->show();
}
if (!msgInfo.attachmentLink.empty()) {
ui->actionAttachmentCopyLinkLocation->setData(QString::fromUtf8(msgInfo.attachmentLink.c_str()));
ui->actionAttachmentCopyLinkLocation->setEnabled(true);
}
} else {
ui->pictureSplitter->hide();
ui->msgText->show();
}
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()), RSHTML_FORMATTEXT_EMBED_LINKS); QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
ui->msgText->setHtml(msgTxt); if (ui->pictureSplitter->isVisible()) {
ui->msgTextSplitter->setHtml(msgTxt);
ui->msgText->clear();
} else {
ui->msgText->setHtml(msgTxt);
ui->msgTextSplitter->clear();
ui->attachmentLabel->clear();
}
ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str())); ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str()));
ui->linkButton->setEnabled(!msgInfo.link.empty()); ui->linkButton->setEnabled(!msgInfo.link.empty());
@ -742,11 +795,11 @@ void FeedReaderMessageWidget::toggleMsgText()
void FeedReaderMessageWidget::toggleMsgText_internal() void FeedReaderMessageWidget::toggleMsgText_internal()
{ {
if (ui->expandButton->isChecked()) { if (ui->expandButton->isChecked()) {
ui->msgText->setVisible(true); ui->horizontalLayoutWidget->setVisible(true);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png")));
ui->expandButton->setToolTip(tr("Hide")); ui->expandButton->setToolTip(tr("Hide"));
} else { } else {
ui->msgText->setVisible(false); ui->horizontalLayoutWidget->setVisible(false);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png"))); ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png")));
ui->expandButton->setToolTip(tr("Expand")); ui->expandButton->setToolTip(tr("Expand"));
} }
@ -969,3 +1022,22 @@ void FeedReaderMessageWidget::addToPosted()
msgDialog->setLink(QString::fromUtf8(msgInfo.link.c_str())); msgDialog->setLink(QString::fromUtf8(msgInfo.link.c_str()));
msgDialog->show(); msgDialog->show();
} }
void FeedReaderMessageWidget::attachmentCopyLinkLocation()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QVariant data = action->data();
if (!data.isValid()) {
return;
}
if (data.type() != QVariant::String) {
return;
}
QApplication::clipboard()->setText(data.toString());
}

View File

@ -77,6 +77,7 @@ private slots:
void fillPostedMenu(); void fillPostedMenu();
void addToForum(); void addToForum();
void addToPosted(); void addToPosted();
void attachmentCopyLinkLocation();
/* FeedReaderNotify */ /* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type); void feedChanged(uint32_t feedId, int type);
@ -92,6 +93,7 @@ private:
void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn); void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn);
void filterItem(QTreeWidgetItem *item); void filterItem(QTreeWidgetItem *item);
void toggleMsgText_internal(); void toggleMsgText_internal();
void clearMessage();
bool mProcessSettings; bool mProcessSettings;
RSTreeWidgetItemCompareRole *mMsgCompareRole; RSTreeWidgetItemCompareRole *mMsgCompareRole;

View File

@ -182,7 +182,7 @@
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../retroshare-gui/src/gui/images.qrc"> <iconset>
<normaloff>:/images/message-state-header.png</normaloff>:/images/message-state-header.png</iconset> <normaloff>:/images/message-state-header.png</normaloff>:/images/message-state-header.png</iconset>
</property> </property>
</column> </column>
@ -245,7 +245,7 @@
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../retroshare-gui/src/gui/images.qrc"> <iconset>
<normaloff>:/images/edit_remove24.png</normaloff>:/images/edit_remove24.png</iconset> <normaloff>:/images/edit_remove24.png</normaloff>:/images/edit_remove24.png</iconset>
</property> </property>
<property name="checkable"> <property name="checkable">
@ -260,20 +260,63 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="RSTextBrowser" name="msgText"> <widget class="QWidget" name="horizontalLayoutWidget">
<property name="sizePolicy"> <layout class="QHBoxLayout" name="pictureHorizontalLayout">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <item>
<horstretch>0</horstretch> <widget class="RSTextBrowser" name="msgText">
<verstretch>10</verstretch> <property name="sizePolicy">
</sizepolicy> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
</property> <horstretch>0</horstretch>
<property name="textInteractionFlags"> <verstretch>10</verstretch>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </sizepolicy>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="pictureSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="AspectRatioPixmapLabel" name="attachmentLabel">
<property name="text">
<string notr="true">pictureLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="RSTextBrowser" name="msgTextSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</widget>
</item>
</layout>
</widget> </widget>
</widget> </widget>
</item> </item>
</layout> </layout>
<action name="actionAttachmentCopyLinkLocation">
<property name="text">
<string>Copy Link Location</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -297,10 +340,14 @@
<header location="global">gui/common/ElidedLabel.h</header> <header location="global">gui/common/ElidedLabel.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>AspectRatioPixmapLabel</class>
<extends>QLabel</extends>
<header location="global">util/AspectRatioPixmapLabel.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="FeedReader_images.qrc"/> <include location="FeedReader_images.qrc"/>
<include location="../../../retroshare-gui/src/gui/images.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -34,7 +34,7 @@ void FeedReaderNotify::notifyMsgChanged(uint32_t feedId, const std::string &msgI
emit msgChanged(feedId, QString::fromStdString(msgId), type); emit msgChanged(feedId, QString::fromStdString(msgId), type);
} }
void FeedReaderNotify::notifyShrinkImage() void FeedReaderNotify::notifyOptimizeImage()
{ {
emit shrinkImage(); emit optimizeImage();
} }

View File

@ -34,12 +34,12 @@ public:
/* RsFeedReaderNotify */ /* RsFeedReaderNotify */
virtual void notifyFeedChanged(uint32_t feedId, int type); virtual void notifyFeedChanged(uint32_t feedId, int type);
virtual void notifyMsgChanged(uint32_t feedId, const std::string &msgId, int type); virtual void notifyMsgChanged(uint32_t feedId, const std::string &msgId, int type);
virtual void notifyShrinkImage(); virtual void notifyOptimizeImage();
signals: signals:
void feedChanged(uint32_t feedId, int type); void feedChanged(uint32_t feedId, int type);
void msgChanged(uint32_t feedId, const QString &msgId, int type); void msgChanged(uint32_t feedId, const QString &msgId, int type);
void shrinkImage(); void optimizeImage();
}; };
#endif #endif

View File

@ -185,6 +185,9 @@ public:
std::string description; std::string description;
std::string descriptionTransformed; std::string descriptionTransformed;
time_t pubDate; time_t pubDate;
std::string attachmentLink;
std::vector<unsigned char> attachment;
std::string attachmentMimeType;
struct { struct {
bool isnew : 1; bool isnew : 1;
@ -193,24 +196,28 @@ public:
} flag; } flag;
}; };
class FeedReaderShrinkImageTask class FeedReaderOptimizeImageTask
{ {
public: public:
enum Type { enum Type {
POSTED POSTED,
SIZE
}; };
public: public:
Type mType; Type mType;
std::vector<unsigned char> mImage; std::vector<unsigned char> mImage;
std::string mMimeType;
std::vector<unsigned char> mImageResult; std::vector<unsigned char> mImageResult;
std::string mMimeTypeResult;
bool mResult; bool mResult;
public: public:
FeedReaderShrinkImageTask(Type type, const std::vector<unsigned char> &image) FeedReaderOptimizeImageTask(Type type, const std::vector<unsigned char> &image, const std::string &mimeType)
{ {
mType = type; mType = type;
mImage = image; mImage = image;
mMimeType = mimeType;
mResult = false; mResult = false;
} }
}; };
@ -222,7 +229,7 @@ public:
virtual void notifyFeedChanged(uint32_t /*feedId*/, int /*type*/) {} virtual void notifyFeedChanged(uint32_t /*feedId*/, int /*type*/) {}
virtual void notifyMsgChanged(uint32_t /*feedId*/, const std::string &/*msgId*/, int /*type*/) {} virtual void notifyMsgChanged(uint32_t /*feedId*/, const std::string &/*msgId*/, int /*type*/) {}
virtual void notifyShrinkImage() {} virtual void notifyOptimizeImage() {}
}; };
class RsFeedReader class RsFeedReader
@ -268,8 +275,8 @@ public:
virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn) = 0; virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn) = 0;
virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn) = 0; virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn) = 0;
virtual FeedReaderShrinkImageTask *getShrinkImageTask() = 0; virtual FeedReaderOptimizeImageTask *getOptimizeImageTask() = 0;
virtual void setShrinkImageTaskResult(FeedReaderShrinkImageTask *shrinkImageTask) = 0; virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask) = 0;
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString) = 0; virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString) = 0;
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString) = 0; virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString) = 0;

View File

@ -233,6 +233,13 @@ static void feedMsgToInfo(const RsFeedReaderMsg *msg, FeedMsgInfo &info)
info.description = msg->description; info.description = msg->description;
info.descriptionTransformed = msg->descriptionTransformed; info.descriptionTransformed = msg->descriptionTransformed;
info.pubDate = msg->pubDate; info.pubDate = msg->pubDate;
info.attachmentLink = msg->attachmentLink;
if (!msg->attachment.empty()) {
p3FeedReaderThread::fromBase64(msg->attachment, info.attachment);
} else {
info.attachment.clear();
}
info.attachmentMimeType = msg->attachmentMimeType;
info.flag.isnew = (msg->flag & RS_FEEDMSG_FLAG_NEW); info.flag.isnew = (msg->flag & RS_FEEDMSG_FLAG_NEW);
info.flag.read = (msg->flag & RS_FEEDMSG_FLAG_READ); info.flag.read = (msg->flag & RS_FEEDMSG_FLAG_READ);
@ -1264,7 +1271,7 @@ bool p3FeedReader::setMessageRead(uint32_t feedId, const std::string &msgId, boo
} }
if (changed) { if (changed) {
IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_NOW); IndicateConfigChanged(RsConfigMgr::CheckPriority::SAVE_OFTEN);
if (mNotify) { if (mNotify) {
mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD); mNotify->notifyFeedChanged(feedId, NOTIFY_TYPE_MOD);
mNotify->notifyMsgChanged(feedId, msgId, NOTIFY_TYPE_MOD); mNotify->notifyMsgChanged(feedId, msgId, NOTIFY_TYPE_MOD);
@ -1450,19 +1457,19 @@ int p3FeedReader::tick()
} }
// check images // check images
bool imageToShrink = false; bool imageToOptimze = false;
{ {
RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
imageToShrink = !mImages.empty(); imageToOptimze = !mImages.empty();
} }
if (mNotify) { if (mNotify) {
for (it = notifyIds.begin(); it != notifyIds.end(); ++it) { for (it = notifyIds.begin(); it != notifyIds.end(); ++it) {
mNotify->notifyFeedChanged(*it, NOTIFY_TYPE_MOD); mNotify->notifyFeedChanged(*it, NOTIFY_TYPE_MOD);
} }
if (imageToShrink) { if (imageToOptimze) {
mNotify->notifyShrinkImage(); mNotify->notifyOptimizeImage();
} }
} }
@ -2110,6 +2117,9 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedRea
} }
miNew->description.clear(); miNew->description.clear();
miNew->descriptionTransformed.clear(); miNew->descriptionTransformed.clear();
miNew->attachmentLink.clear();
miNew->attachment.clear();
miNew->attachmentMimeType.clear();
} else { } else {
miNew->flag = RS_FEEDMSG_FLAG_NEW; miNew->flag = RS_FEEDMSG_FLAG_NEW;
addedMsgs.push_back(miNew->msgId); addedMsgs.push_back(miNew->msgId);
@ -2151,6 +2161,25 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedRea
if (!mi.link.empty()) { if (!mi.link.empty()) {
description += "<br><a href=\"" + mi.link + "\">" + mi.link + "</a>"; description += "<br><a href=\"" + mi.link + "\">" + mi.link + "</a>";
} }
if (!mi.attachmentBinary.empty()) {
if (p3FeedReaderThread::isContentType(mi.attachmentMimeType, "image/")) {
/* add attachement to description */
// optimize image
std::vector<unsigned char> optimizedImage;
std::string optimizedMimeType;
if (optimizeImage(FeedReaderOptimizeImageTask::SIZE, mi.attachmentBinary, mi.attachmentBinaryMimeType, optimizedImage, optimizedMimeType)) {
std::string base64;
if (p3FeedReaderThread::toBase64(optimizedImage, base64)) {
std::string imageBase64;
rs_sprintf(imageBase64, "data:%s;base64,%s", optimizedMimeType.c_str(), base64.c_str());
description += "<br><img src=\"" + imageBase64 + "\"/>";
}
}
}
}
forumMsg.mMsg = description; forumMsg.mMsg = description;
uint32_t token; uint32_t token;
@ -2197,10 +2226,11 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedRea
if (!mi.postedFirstImage.empty()) { if (!mi.postedFirstImage.empty()) {
/* use first image as image for posted and description without image as notes */ /* use first image as image for posted and description without image as notes */
if (feedFlag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE) { if (feedFlag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE) {
// shrink image // optimize image
std::vector<unsigned char> shrinkedImage; std::vector<unsigned char> optimizedImage;
if (shrinkImage(FeedReaderShrinkImageTask::POSTED, mi.postedFirstImage, shrinkedImage)) { std::string optimizedMimeType;
postedPost.mImage.copy(shrinkedImage.data(), shrinkedImage.size()); if (optimizeImage(FeedReaderOptimizeImageTask::POSTED, mi.postedFirstImage, mi.postedFirstImageMimeType, optimizedImage, optimizedMimeType)) {
postedPost.mImage.copy(optimizedImage.data(), optimizedImage.size());
} }
} else { } else {
postedPost.mImage.copy(mi.postedFirstImage.data(), mi.postedFirstImage.size()); postedPost.mImage.copy(mi.postedFirstImage.data(), mi.postedFirstImage.size());
@ -2219,6 +2249,23 @@ void p3FeedReader::onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedRea
} }
} else { } else {
description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed; description = mi.descriptionTransformed.empty() ? mi.description : mi.descriptionTransformed;
if (!mi.attachmentBinary.empty()) {
if (p3FeedReaderThread::isContentType(mi.attachmentMimeType, "image/")) {
/* use attachement as image */
if (feedFlag & RS_FEED_FLAG_POSTED_SHRINK_IMAGE) {
// optimize image
std::vector<unsigned char> optimizedImage;
std::string optimizedMimeType;
if (optimizeImage(FeedReaderOptimizeImageTask::POSTED, mi.attachmentBinary, mi.attachmentBinaryMimeType, optimizedImage, optimizedMimeType)) {
postedPost.mImage.copy(optimizedImage.data(), optimizedImage.size());
}
} else {
postedPost.mImage.copy(mi.attachmentBinary.data(), mi.attachmentBinary.size());
}
}
}
} }
postedPost.mNotes = description; postedPost.mNotes = description;
@ -2662,17 +2709,17 @@ bool p3FeedReader::getPostedGroups(std::vector<RsPostedGroup> &groups, bool only
return true; return true;
} }
bool p3FeedReader::shrinkImage(FeedReaderShrinkImageTask::Type type, const std::vector<unsigned char> &image, std::vector<unsigned char> &resultImage) bool p3FeedReader::optimizeImage(FeedReaderOptimizeImageTask::Type type, const std::vector<unsigned char> &image, const std::string &mimeType, std::vector<unsigned char> &resultImage, std::string &resultMimeType)
{ {
if (!mNotify) { if (!mNotify) {
return false; return false;
} }
FeedReaderShrinkImageTask *shrinkImageTask = new FeedReaderShrinkImageTask(type, image); FeedReaderOptimizeImageTask *optimizeImageTask = new FeedReaderOptimizeImageTask(type, image, mimeType);
{ {
RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
mImages.push_back(shrinkImageTask); mImages.push_back(optimizeImageTask);
} }
/* Wait until task is complete */ /* Wait until task is complete */
@ -2686,11 +2733,11 @@ bool p3FeedReader::shrinkImage(FeedReaderShrinkImageTask::Type type, const std::
if (++nSeconds >= 30) { if (++nSeconds >= 30) {
// timeout // timeout
std::list<FeedReaderShrinkImageTask*>::iterator it = std::find(mImages.begin(), mImages.end(), shrinkImageTask); std::list<FeedReaderOptimizeImageTask*>::iterator it = std::find(mImages.begin(), mImages.end(), optimizeImageTask);
if (it != mImages.end()) { if (it != mImages.end()) {
mImages.erase(it); mImages.erase(it);
delete(shrinkImageTask); delete(optimizeImageTask);
return false; return false;
} }
@ -2701,16 +2748,17 @@ bool p3FeedReader::shrinkImage(FeedReaderShrinkImageTask::Type type, const std::
{ {
RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
std::list<FeedReaderShrinkImageTask*>::iterator it = std::find(mResultImages.begin(), mResultImages.end(), shrinkImageTask); std::list<FeedReaderOptimizeImageTask*>::iterator it = std::find(mResultImages.begin(), mResultImages.end(), optimizeImageTask);
if (it != mResultImages.end()) { if (it != mResultImages.end()) {
mResultImages.erase(it); mResultImages.erase(it);
bool result = shrinkImageTask->mResult; bool result = optimizeImageTask->mResult;
if (result) { if (result) {
resultImage = shrinkImageTask->mImageResult; resultImage = optimizeImageTask->mImageResult;
resultMimeType = optimizeImageTask->mMimeTypeResult;
} }
delete(shrinkImageTask); delete(optimizeImageTask);
return result; return result;
} }
@ -2720,7 +2768,7 @@ bool p3FeedReader::shrinkImage(FeedReaderShrinkImageTask::Type type, const std::
return false; return false;
} }
FeedReaderShrinkImageTask *p3FeedReader::getShrinkImageTask() FeedReaderOptimizeImageTask *p3FeedReader::getOptimizeImageTask()
{ {
RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
@ -2728,19 +2776,19 @@ FeedReaderShrinkImageTask *p3FeedReader::getShrinkImageTask()
return NULL; return NULL;
} }
FeedReaderShrinkImageTask *imageResize = mImages.front(); FeedReaderOptimizeImageTask *imageResize = mImages.front();
mImages.pop_front(); mImages.pop_front();
return imageResize; return imageResize;
} }
void p3FeedReader::setShrinkImageTaskResult(FeedReaderShrinkImageTask *shrinkImageTask) void p3FeedReader::setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask)
{ {
if (!shrinkImageTask) { if (!optimizeImageTask) {
return; return;
} }
RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/ RsStackMutex stack(mImageMutex); /******* LOCK STACK MUTEX *********/
mResultImages.push_back(shrinkImageTask); mResultImages.push_back(optimizeImageTask);
} }

View File

@ -80,8 +80,8 @@ public:
virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn); virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn);
virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn); virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn);
virtual FeedReaderShrinkImageTask *getShrinkImageTask(); virtual FeedReaderOptimizeImageTask *getOptimizeImageTask();
virtual void setShrinkImageTaskResult(FeedReaderShrinkImageTask *shrinkedImageTask); virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask);
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString); virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString);
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString); virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString);
@ -107,7 +107,7 @@ public:
bool getPostedGroup(const RsGxsGroupId &groupId, RsPostedGroup &postedGroup); bool getPostedGroup(const RsGxsGroupId &groupId, RsPostedGroup &postedGroup);
bool updatePostedGroup(const RsPostedGroup &postedGroup, const std::string &groupName, const std::string &groupDescription); bool updatePostedGroup(const RsPostedGroup &postedGroup, const std::string &groupName, const std::string &groupDescription);
bool waitForToken(RsGxsIfaceHelper *interface, uint32_t token); bool waitForToken(RsGxsIfaceHelper *interface, uint32_t token);
bool shrinkImage(FeedReaderShrinkImageTask::Type type, const std::vector<unsigned char> &image, std::vector<unsigned char> &resultImage); bool optimizeImage(FeedReaderOptimizeImageTask::Type type, const std::vector<unsigned char> &image, const std::string &mimeType, std::vector<unsigned char> &resultImage, std::string &resultMimeType);
protected: protected:
/****************** p3Config STUFF *******************/ /****************** p3Config STUFF *******************/
@ -150,8 +150,8 @@ private:
std::list<uint32_t> mProcessFeeds; std::list<uint32_t> mProcessFeeds;
RsMutex mImageMutex; RsMutex mImageMutex;
std::list<FeedReaderShrinkImageTask*> mImages; std::list<FeedReaderOptimizeImageTask*> mImages;
std::list<FeedReaderShrinkImageTask*> mResultImages; std::list<FeedReaderOptimizeImageTask*> mResultImages;
RsMutex mPreviewMutex; RsMutex mPreviewMutex;
p3FeedReaderThread *mPreviewDownloadThread; p3FeedReaderThread *mPreviewDownloadThread;

View File

@ -89,7 +89,7 @@ void p3FeedReaderThread::threadTick()
/* first, filter the messages */ /* first, filter the messages */
mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs); mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs);
if (isRunning()) { if (isRunning()) {
/* second, process the descriptions */ /* second, process the descriptions and attachment */
for (it = msgs.begin(); it != msgs.end(); ) { for (it = msgs.begin(); it != msgs.end(); ) {
if (!isRunning()) { if (!isRunning()) {
break; break;
@ -153,12 +153,12 @@ void p3FeedReaderThread::threadTick()
/****************************** Download ***********************************/ /****************************** Download ***********************************/
/***************************************************************************/ /***************************************************************************/
static bool isContentType(const std::string &contentType, const char *type) bool p3FeedReaderThread::isContentType(const std::string &contentType, const char *type)
{ {
return (strncasecmp(contentType.c_str(), type, strlen(type)) == 0); return (strncasecmp(contentType.c_str(), type, strlen(type)) == 0);
} }
static bool toBase64(const std::vector<unsigned char> &data, std::string &base64) bool p3FeedReaderThread::toBase64(const std::vector<unsigned char> &data, std::string &base64)
{ {
bool result = false; bool result = false;
@ -187,6 +187,28 @@ static bool toBase64(const std::vector<unsigned char> &data, std::string &base64
return result; return result;
} }
bool p3FeedReaderThread::fromBase64(const std::string &base64, std::vector<unsigned char> &data)
{
bool result = false;
BIO *b64 = BIO_new(BIO_f_base64());
if (b64) {
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO *source = BIO_new_mem_buf(base64.c_str(), -1); // read-only source
if (source) {
BIO_push(b64, source);
const int maxlen = base64.length() / 4 * 3 + 1;
data.resize(maxlen);
const int len = BIO_read(b64, data.data(), maxlen);
data.resize(len);
result = true;
}
BIO_free_all(b64);
}
return result;
}
static std::string getBaseLink(std::string link) static std::string getBaseLink(std::string link)
{ {
size_t found = link.rfind('/'); size_t found = link.rfind('/');
@ -254,12 +276,12 @@ static bool getFavicon(CURLWrapper &CURL, const std::string &url, std::string &i
if (code == CURLE_OK) { if (code == CURLE_OK) {
if (CURL.responseCode() == 200) { if (CURL.responseCode() == 200) {
std::string contentType = CURL.contentType(); std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/") || if (p3FeedReaderThread::isContentType(contentType, "image/") ||
isContentType(contentType, "application/octet-stream") || p3FeedReaderThread::isContentType(contentType, "application/octet-stream") ||
isContentType(contentType, "text/plain")) { p3FeedReaderThread::isContentType(contentType, "text/plain")) {
if (!vicon.empty()) { if (!vicon.empty()) {
#warning p3FeedReaderThread.cc TODO thunder2: check it #warning p3FeedReaderThread.cc TODO thunder2: check it
result = toBase64(vicon, icon); result = p3FeedReaderThread::toBase64(vicon, icon);
} }
} }
} }
@ -971,6 +993,19 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
item->pubDate = time(NULL); item->pubDate = time(NULL);
} }
if (feedFormat == FORMAT_RSS) {
/* <enclosure url="" type=""></enclosure> */
xmlNodePtr enclosure = xml.findNode(node->children, "enclosure", false);
if (enclosure) {
std::string enclosureMimeType = xml.getAttr(enclosure, "type");
std::string enclosureUrl = xml.getAttr(enclosure, "url");
if (!enclosureUrl.empty()) {
item->attachmentLink = enclosureUrl;
item->attachmentMimeType = enclosureMimeType;
}
}
}
entries.push_back(item); entries.push_back(item);
} }
} else { } else {
@ -1025,6 +1060,40 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK; RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
std::string proxy = getProxyForFeed(feed); std::string proxy = getProxyForFeed(feed);
/* attachment */
if (!msg->attachmentLink.empty()) {
if (isContentType(msg->attachmentMimeType, "image/")) {
CURLWrapper CURL(proxy);
CURLcode code = CURL.downloadBinary(msg->attachmentLink, msg->attachmentBinary);
if (code == CURLE_OK && CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/")) {
msg->attachmentBinaryMimeType = contentType;
bool forum = (feed.flag & RS_FEED_FLAG_FORUM) && !feed.preview;
bool posted = (feed.flag & RS_FEED_FLAG_POSTED) && !feed.preview;
if (!forum && ! posted) {
/* no need to optimize image */
std::vector<unsigned char> optimizedBinary;
std::string optimizedMimeType;
if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, msg->attachmentBinary, msg->attachmentBinaryMimeType, optimizedBinary, optimizedMimeType)) {
if (toBase64(optimizedBinary, msg->attachment)) {
msg->attachmentMimeType = optimizedMimeType;
} else {
msg->attachment.clear();
}
}
}
} else {
msg->attachmentBinary.clear();
}
} else {
msg->attachmentBinary.clear();
}
}
}
std::string url; std::string url;
if (feed.flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE) { if (feed.flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE) {
#ifdef FEEDREADER_DEBUG #ifdef FEEDREADER_DEBUG
@ -1083,6 +1152,10 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (isRunning()) { if (isRunning()) {
/* process description */ /* process description */
bool processPostedFirstImage = (feed.flag & RS_FEED_FLAG_POSTED_FIRST_IMAGE) ? TRUE : FALSE; bool processPostedFirstImage = (feed.flag & RS_FEED_FLAG_POSTED_FIRST_IMAGE) ? TRUE : FALSE;
if (!msg->attachmentBinary.empty()) {
/* use attachment as image */
processPostedFirstImage = FALSE;
}
//long todo; // encoding //long todo; // encoding
HTMLWrapper html; HTMLWrapper html;
@ -1215,20 +1288,24 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (code == CURLE_OK && CURL.responseCode() == 200) { if (code == CURLE_OK && CURL.responseCode() == 200) {
std::string contentType = CURL.contentType(); std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/")) { if (isContentType(contentType, "image/")) {
std::string base64; std::vector<unsigned char> optimizedData;
if (toBase64(data, base64)) { std::string optimizedMimeType;
std::string imageBase64; if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, data, contentType, optimizedData, optimizedMimeType)) {
rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str()); std::string base64;
if (html.setAttr(node, "src", imageBase64.c_str())) { if (toBase64(optimizedData, base64)) {
removeImage = false; std::string imageBase64;
rs_sprintf(imageBase64, "data:%s;base64,%s", optimizedMimeType.c_str(), base64.c_str());
if (processPostedFirstImage && postedFirstImageNode == NULL) { if (html.setAttr(node, "src", imageBase64.c_str())) {
/* set first image */ removeImage = false;
msg->postedFirstImage = data;
postedFirstImageNode = node;
} }
} }
} }
if (processPostedFirstImage && postedFirstImageNode == NULL) {
/* set first image */
msg->postedFirstImage = data;
msg->postedFirstImageMimeType = contentType;
postedFirstImageNode = node;
}
} }
} }
} }

View File

@ -54,6 +54,11 @@ public:
static RsFeedReaderErrorState processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString); static RsFeedReaderErrorState processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString);
static RsFeedReaderErrorState processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString); static RsFeedReaderErrorState processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString);
static bool isContentType(const std::string &contentType, const char *type);
static bool toBase64(const std::vector<unsigned char> &data, std::string &base64);
static bool fromBase64(const std::string &base64, std::vector<unsigned char> &data);
private: private:
virtual void threadTick() override; /// @see RsTickingThread virtual void threadTick() override; /// @see RsTickingThread

View File

@ -274,6 +274,9 @@ void RsFeedReaderMsg::clear()
descriptionTransformed.clear(); descriptionTransformed.clear();
pubDate = 0; pubDate = 0;
flag = 0; flag = 0;
attachmentLink.clear();
attachment.clear();
attachmentMimeType.clear();
} }
std::ostream &RsFeedReaderMsg::print(std::ostream &out, uint16_t /*indent*/) std::ostream &RsFeedReaderMsg::print(std::ostream &out, uint16_t /*indent*/)
@ -294,6 +297,9 @@ uint32_t RsFeedReaderSerialiser::sizeMsg(RsFeedReaderMsg *item)
s += GetTlvStringSize(item->descriptionTransformed); s += GetTlvStringSize(item->descriptionTransformed);
s += sizeof(uint32_t); /* pubDate */ s += sizeof(uint32_t); /* pubDate */
s += sizeof(uint32_t); /* flag */ s += sizeof(uint32_t); /* flag */
s += GetTlvStringSize(item->attachmentLink);
s += GetTlvStringSize(item->attachment);
s += GetTlvStringSize(item->attachmentMimeType);
return s; return s;
} }
@ -317,7 +323,7 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
offset += 8; offset += 8;
/* add values */ /* add values */
ok &= setRawUInt16(data, tlvsize, &offset, 2); /* version */ ok &= setRawUInt16(data, tlvsize, &offset, 3); /* version */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId);
ok &= setRawUInt32(data, tlvsize, &offset, item->feedId); ok &= setRawUInt32(data, tlvsize, &offset, item->feedId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title);
@ -327,6 +333,9 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed); ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed);
ok &= setRawUInt32(data, tlvsize, &offset, item->pubDate); ok &= setRawUInt32(data, tlvsize, &offset, item->pubDate);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag); ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
if (offset != tlvsize) if (offset != tlvsize)
{ {
@ -390,6 +399,11 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk
} }
ok &= getRawUInt32(data, rssize, &offset, (uint32_t*) &(item->pubDate)); ok &= getRawUInt32(data, rssize, &offset, (uint32_t*) &(item->pubDate));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag)); ok &= getRawUInt32(data, rssize, &offset, &(item->flag));
if (version >= 3) {
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
}
if (offset != rssize) if (offset != rssize)
{ {

View File

@ -124,9 +124,15 @@ public:
std::string descriptionTransformed; std::string descriptionTransformed;
time_t pubDate; time_t pubDate;
uint32_t flag; // RS_FEEDMSG_FLAG_... uint32_t flag; // RS_FEEDMSG_FLAG_...
std::string attachmentLink;
std::string attachment; // binary as base64
std::string attachmentMimeType;
// Only in memory when receiving messages // Only in memory when receiving messages
std::vector<unsigned char> attachmentBinary;
std::string attachmentBinaryMimeType;
std::vector<unsigned char> postedFirstImage; std::vector<unsigned char> postedFirstImage;
std::string postedFirstImageMimeType;
std::string postedDescriptionWithoutFirstImage; std::string postedDescriptionWithoutFirstImage;
}; };