1 /* ============================================================
2 * QuiteRSS is a open-source cross-platform RSS/Atom news feeds reader
3 * Copyright (C) 2011-2020 QuiteRSS Team <quiterssteam@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "addfeedwizard.h"
19 
20 #include "mainapplication.h"
21 #include "addfolderdialog.h"
22 #include "authenticationdialog.h"
23 #include "toolbutton.h"
24 #include "settings.h"
25 
26 #include <QDomDocument>
27 #include <qzregexp.h>
28 
29 extern QString kCreateNewsTableQuery;
30 
AddFeedWizard(QWidget * parent,int curFolderId)31 AddFeedWizard::AddFeedWizard(QWidget *parent, int curFolderId)
32   : QWizard(parent),
33     curFolderId_(curFolderId)
34 {
35   setModal(true);
36   setWindowFlags (windowFlags() & ~Qt::WindowContextHelpButtonHint);
37   setWindowTitle(tr("Add Feed"));
38   setWizardStyle(QWizard::ModernStyle);
39   setOptions(QWizard::HaveFinishButtonOnEarlyPages |
40              QWizard::NoBackButtonOnStartPage);
41 
42   addPage(createUrlFeedPage());
43   addPage(createNameFeedPage());
44 
45   updateFeeds_ = new UpdateFeeds(this, true);
46 
47   connect(button(QWizard::BackButton), SIGNAL(clicked()),
48           this, SLOT(backButtonClicked()));
49   connect(button(QWizard::NextButton), SIGNAL(clicked()),
50           this, SLOT(nextButtonClicked()));
51   connect(button(QWizard::FinishButton), SIGNAL(clicked()),
52           this, SLOT(finishButtonClicked()));
53   connect(this, SIGNAL(currentIdChanged(int)),
54           SLOT(slotCurrentIdChanged(int)),
55           Qt::QueuedConnection);
56   resize(400, 300);
57 
58   Settings settings;
59   restoreGeometry(settings.value("addFeedWizard/geometry").toByteArray());
60 }
61 
~AddFeedWizard()62 AddFeedWizard::~AddFeedWizard()
63 {
64   Settings settings;
65   settings.setValue("addFeedWizard/geometry", saveGeometry());
66 
67   updateFeeds_->disconnectObjects();
68   delete updateFeeds_;
69 }
70 
done(int result)71 /*virtual*/ void AddFeedWizard::done(int result)
72 {
73   if (result == QDialog::Rejected) {
74     if (progressBar_->isVisible() || (currentId() == 1))
75       deleteFeed();
76   }
77   QWizard::done(result);
78 }
79 
changeEvent(QEvent * event)80 void AddFeedWizard::changeEvent(QEvent *event)
81 {
82   if ((event->type() == QEvent::ActivationChange) &&
83       isActiveWindow() && (currentId() == 0) && urlFeedEdit_->isEnabled()) {
84     QClipboard *clipboard_ = QApplication::clipboard();
85     QString clipboardStr = clipboard_->text().left(8);
86     if (clipboardStr.contains("http://", Qt::CaseInsensitive) ||
87         clipboardStr.contains("https://", Qt::CaseInsensitive) ||
88         clipboardStr.contains("www.", Qt::CaseInsensitive) ||
89         clipboardStr.contains("feed://", Qt::CaseInsensitive) ||
90         clipboardStr.contains("file://", Qt::CaseInsensitive)) {
91       urlFeedEdit_->setText(clipboard_->text());
92       urlFeedEdit_->selectAll();
93       urlFeedEdit_->setFocus();
94     }
95   }
96 }
97 
createUrlFeedPage()98 QWizardPage *AddFeedWizard::createUrlFeedPage()
99 {
100   QWizardPage *page = new QWizardPage;
101   page->setTitle(tr("Create New Feed"));
102 
103   selectedPage = false;
104   finishOn = false;
105 
106   urlFeedEdit_ = new LineEdit(this);
107   urlFeedEdit_->setText("http://");
108 
109   titleFeedAsName_ = new QCheckBox(
110         tr("Use title of the feed as displayed name"), this);
111   titleFeedAsName_->setChecked(true);
112 
113   authentication_ = new QGroupBox(this);
114   authentication_->setTitle(tr("Server requires authentication:"));
115   authentication_->setCheckable(true);
116   authentication_->setChecked(false);
117 
118   user_ = new LineEdit(this);
119   pass_ = new LineEdit(this);
120   pass_->setEchoMode(QLineEdit::Password);
121 
122   QGridLayout *authenticationLayout = new QGridLayout();
123   authenticationLayout->addWidget(new QLabel(tr("Username:")), 0, 0);
124   authenticationLayout->addWidget(user_, 0, 1);
125   authenticationLayout->addWidget(new QLabel(tr("Password:")), 1, 0);
126   authenticationLayout->addWidget(pass_, 1, 1);
127 
128   authentication_->setLayout(authenticationLayout);
129 
130   QLabel *iconWarning = new QLabel(this);
131   iconWarning->setPixmap(QPixmap(":/images/warning"));
132   textWarning = new QLabel(this);
133   QFont font = textWarning->font();
134   font.setBold(true);
135   textWarning->setFont(font);
136 
137   QHBoxLayout *warningLayout = new QHBoxLayout();
138   warningLayout->setMargin(0);
139   warningLayout->addWidget(iconWarning);
140   warningLayout->addWidget(textWarning, 1);
141 
142   warningWidget_ = new QWidget(this);
143   warningWidget_->setLayout(warningLayout);
144 
145   progressBar_ = new QProgressBar(this);
146   progressBar_->setObjectName("progressBar_");
147   progressBar_->setTextVisible(false);
148   progressBar_->setFixedHeight(15);
149   progressBar_->setMinimum(0);
150   progressBar_->setMaximum(0);
151   progressBar_->setVisible(false);
152 
153   QVBoxLayout *layout = new QVBoxLayout;
154   layout->addWidget(new QLabel(tr("Feed URL or website address:")));
155   layout->addWidget(urlFeedEdit_);
156   layout->addWidget(titleFeedAsName_);
157   layout->addSpacing(10);
158   layout->addWidget(authentication_);
159   layout->addStretch(1);
160   layout->addWidget(warningWidget_);
161   layout->addWidget(progressBar_);
162   page->setLayout(layout);
163 
164   connect(urlFeedEdit_, SIGNAL(textChanged(const QString&)),
165           this, SLOT(urlFeedEditChanged(const QString&)));
166   connect(titleFeedAsName_, SIGNAL(stateChanged(int)),
167           this, SLOT(titleFeedAsNameStateChanged(int)));
168 
169   return page;
170 }
171 
createNameFeedPage()172 QWizardPage *AddFeedWizard::createNameFeedPage()
173 {
174   QWizardPage *page = new QWizardPage;
175   page->setTitle(tr("Create New Feed"));
176   page->setFinalPage(false);
177 
178   nameFeedEdit_ = new LineEdit(this);
179 
180   foldersTree_ = new QTreeWidget(this);
181   foldersTree_->setObjectName("foldersTree_");
182   foldersTree_->setColumnCount(2);
183   foldersTree_->setColumnHidden(1, true);
184   foldersTree_->header()->hide();
185 
186   QStringList treeItem;
187   treeItem << tr("Feeds") << "Id";
188   foldersTree_->setHeaderLabels(treeItem);
189 
190   treeItem.clear();
191   treeItem << tr("All Feeds") << "0";
192   QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem(treeItem);
193   treeWidgetItem->setIcon(0, QIcon(":/images/folder"));
194   foldersTree_->addTopLevelItem(treeWidgetItem);
195   foldersTree_->setCurrentItem(treeWidgetItem);
196 
197   QSqlQuery q;
198   QQueue<int> parentIds;
199   parentIds.enqueue(0);
200   while (!parentIds.empty()) {
201     int parentId = parentIds.dequeue();
202     QString qStr = QString("SELECT text, id FROM feeds WHERE parentId='%1' AND (xmlUrl='' OR xmlUrl IS NULL)").
203         arg(parentId);
204     q.exec(qStr);
205     while (q.next()) {
206       QString folderText = q.value(0).toString();
207       QString folderId = q.value(1).toString();
208 
209       QStringList treeItem;
210       treeItem << folderText << folderId;
211       QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem(treeItem);
212 
213       treeWidgetItem->setIcon(0, QIcon(":/images/folder"));
214 
215       QList<QTreeWidgetItem *> treeItems =
216             foldersTree_->findItems(QString::number(parentId),
217                                                        Qt::MatchFixedString | Qt::MatchRecursive,
218                                                        1);
219       treeItems.at(0)->addChild(treeWidgetItem);
220       if (folderId.toInt() == curFolderId_)
221         foldersTree_->setCurrentItem(treeWidgetItem);
222       parentIds.enqueue(folderId.toInt());
223     }
224   }
225 
226   foldersTree_->expandAll();
227   foldersTree_->sortByColumn(0, Qt::AscendingOrder);
228 
229   ToolButton *newFolderButton = new ToolButton(this);
230   newFolderButton->setIcon(QIcon(":/images/addT"));
231   newFolderButton->setToolTip(tr("New Folder..."));
232   newFolderButton->setAutoRaise(true);
233 
234   QHBoxLayout *newFolderLayout = new QHBoxLayout;
235   newFolderLayout->setMargin(0);
236   newFolderLayout->addWidget(newFolderButton);
237   newFolderLayout->addStretch();
238   QVBoxLayout *newFolderVLayout = new QVBoxLayout;
239   newFolderVLayout->setMargin(2);
240   newFolderVLayout->addStretch();
241   newFolderVLayout->addLayout(newFolderLayout);
242 
243   foldersTree_->setLayout(newFolderVLayout);
244 
245   QVBoxLayout *layout = new QVBoxLayout;
246   layout->addWidget(new QLabel(tr("Displayed name:")));
247   layout->addWidget(nameFeedEdit_);
248   layout->addWidget(new QLabel(tr("Location:")));
249   layout->addWidget(foldersTree_);
250   page->setLayout(layout);
251 
252   connect(nameFeedEdit_, SIGNAL(textChanged(const QString&)),
253           this, SLOT(nameFeedEditChanged(const QString&)));
254   connect(newFolderButton, SIGNAL(clicked()),
255           this, SLOT(newFolder()));
256 
257   return page;
258 }
259 
setUrlFeed(const QString & feedUrl)260 void AddFeedWizard::setUrlFeed(const QString &feedUrl)
261 {
262   urlFeedEdit_->setText(feedUrl);
263 }
264 
urlFeedEditChanged(const QString & text)265 void AddFeedWizard::urlFeedEditChanged(const QString& text)
266 {
267   button(QWizard::NextButton)->setEnabled(
268         !text.isEmpty() && (text != "http://"));
269 
270   bool buttonEnable = false;
271   if (titleFeedAsName_->isChecked() && (text != "http://") &&
272       !text.isEmpty()) {
273     buttonEnable = true;
274   }
275   warningWidget_->setVisible(false);
276   button(QWizard::FinishButton)->setEnabled(buttonEnable);
277 }
278 
titleFeedAsNameStateChanged(int state)279 void AddFeedWizard::titleFeedAsNameStateChanged(int state)
280 {
281   bool buttonEnable = false;
282   if ((state == Qt::Checked) && (urlFeedEdit_->text() != "http://") &&
283       !urlFeedEdit_->text().isEmpty()) {
284     buttonEnable = true;
285   }
286   button(QWizard::FinishButton)->setEnabled(buttonEnable);
287 }
288 
nameFeedEditChanged(const QString & text)289 void AddFeedWizard::nameFeedEditChanged(const QString& text)
290 {
291   button(QWizard::FinishButton)->setEnabled(!text.isEmpty());
292 }
293 
backButtonClicked()294 void AddFeedWizard::backButtonClicked()
295 {
296   deleteFeed();
297   nameFeedEdit_->clear();
298   page(0)->setEnabled(true);
299   selectedPage = false;
300 }
301 
nextButtonClicked()302 void AddFeedWizard::nextButtonClicked()
303 {
304   if (currentId() == 0)
305     addFeed();
306 }
307 
finishButtonClicked()308 void AddFeedWizard::finishButtonClicked()
309 {
310   if (currentId() == 0) {
311     finishOn = true;
312     addFeed();
313   } else if (currentId() == 1) {
314     finish();
315   }
316 }
317 
addFeed()318 void AddFeedWizard::addFeed()
319 {
320   // Set URL-schema for URL-address "http://" or leave it "https://"
321   feedUrlString_ = urlFeedEdit_->text().simplified();
322   if (feedUrlString_.contains("feed:", Qt::CaseInsensitive)) {
323     if (feedUrlString_.contains("https://", Qt::CaseInsensitive)) {
324       feedUrlString_.remove(0, 5);
325       urlFeedEdit_->setText(feedUrlString_);
326     } else {
327       feedUrlString_.remove(0, 7);
328       urlFeedEdit_->setText("http://" + feedUrlString_);
329     }
330   }
331   QUrl feedUrl(urlFeedEdit_->text().simplified());
332   if (feedUrl.scheme().isEmpty()) {
333     feedUrl.setUrl("http://" % urlFeedEdit_->text().simplified());
334   }
335   feedUrlString_ = feedUrl.toString();
336   urlFeedEdit_->setText(feedUrlString_);
337 
338 #if QT_VERSION >= 0x040800
339   if (feedUrl.host().isEmpty() && !feedUrl.isLocalFile()) {
340 #else
341   if (feedUrl.host().isEmpty() && (feedUrl.scheme() != "file")) {
342 #endif
343     textWarning->setText(tr("URL error!"));
344     warningWidget_->setVisible(true);
345     return;
346   }
347 
348   QSqlQuery q;
349   int duplicateFoundId = -1;
350   q.prepare("SELECT id FROM feeds WHERE xmlUrl LIKE :xmlUrl");
351   q.bindValue(":xmlUrl", feedUrlString_);
352   q.exec();
353   if (q.first())
354     duplicateFoundId = q.value(0).toInt();
355 
356   if (0 <= duplicateFoundId) {
357     textWarning->setText(tr("Duplicate feed!"));
358     warningWidget_->setVisible(true);
359   } else {
360     button(QWizard::NextButton)->setEnabled(false);
361     button(QWizard::CancelButton)->setEnabled(false);
362     button(QWizard::FinishButton)->setEnabled(false);
363     page(0)->setEnabled(false);
364     showProgressBar();
365 
366     // Calculate row's number to insert new feed
367     int rowToParent = 0;
368     q.exec("SELECT count(id) FROM feeds WHERE parentId=0");
369     if (q.next()) rowToParent = q.value(0).toInt();
370 
371     int auth = 0;
372     QString userInfo;
373     if (authentication_->isChecked()) {
374       auth = 1;
375       userInfo = QString("%1:%2").arg(user_->text()).arg(pass_->text());
376     }
377 
378     // Insert feed
379     q.prepare("INSERT INTO feeds(xmlUrl, created, rowToParent, authentication) "
380               "VALUES (:feedUrl, :feedCreateTime, :rowToParent, :authentication)");
381     q.bindValue(":feedUrl", feedUrlString_);
382     q.bindValue(":feedCreateTime",
383         QLocale::c().toString(QDateTime::currentDateTimeUtc(), "yyyy-MM-ddTHH:mm:ss"));
384     q.bindValue(":rowToParent", rowToParent);
385     q.bindValue(":authentication", auth);
386     q.exec();
387 
388     feedId_ = q.lastInsertId().toInt();
389     q.finish();
390 
391     if (feedUrlString_.contains(":COOKIE:", Qt::CaseInsensitive)) {
392       int index = feedUrlString_.lastIndexOf(":COOKIE:", -1, Qt::CaseInsensitive);
393       QString cookieStr = feedUrlString_.right(feedUrlString_.length() - index - 8);
394       QStringList cookieStrList = cookieStr.split(";");
395 
396       QList<QNetworkCookie> loadedCookies;
397       foreach (QString cookieStr, cookieStrList) {
398         const QList<QNetworkCookie> &cookieList = QNetworkCookie::parseCookies(cookieStr.toUtf8());
399         if (cookieList.isEmpty()) {
400           continue;
401         }
402         QNetworkCookie cookie = cookieList.at(0);
403         QDateTime date = QDateTime::currentDateTime();
404         date = date.addYears(35);
405         cookie.setExpirationDate(date);
406         loadedCookies.append(cookie);
407       }
408       mainApp->cookieJar()->setCookiesFromUrl(loadedCookies, feedUrlString_);
409     }
410 
411     emit signalRequestUrl(feedId_, feedUrlString_, QDateTime(), userInfo);
412   }
413 }
414 
415 void AddFeedWizard::deleteFeed()
416 {
417   QSqlQuery q;
418   q.exec(QString("DELETE FROM feeds WHERE id='%1'").arg(feedId_));
419   q.exec(QString("DELETE FROM news WHERE feedId='%1'").arg(feedId_));
420 
421   // Correct rowToParent field
422   QList<int> idList;
423   q.exec("SELECT id FROM feeds WHERE parentId=0 ORDER BY rowToParent");
424   while (q.next()) {
425     idList << q.value(0).toInt();
426   }
427   for (int i = 0; i < idList.count(); i++) {
428     q.exec(QString("UPDATE feeds SET rowToParent='%1' WHERE id=='%2'").
429            arg(i).arg(idList.at(i)));
430   }
431   q.finish();
432 }
433 
434 void AddFeedWizard::slotCurrentIdChanged(int idPage)
435 {
436   if (idPage == 0)
437     urlFeedEditChanged(urlFeedEdit_->text());
438   else if (idPage == 1)
439     nameFeedEditChanged(nameFeedEdit_->text());
440 }
441 
442 /*virtual*/ bool AddFeedWizard::validateCurrentPage()
443 {
444   if (!selectedPage) {
445     return false;
446   }
447   return true;
448 }
449 
450 void AddFeedWizard::showProgressBar()
451 {
452   progressBar_->show();
453   QTimer::singleShot(250, this, SLOT(slotProgressBarUpdate()));
454 }
455 
456 void AddFeedWizard::slotProgressBarUpdate()
457 {
458   progressBar_->update();
459   if (progressBar_->isVisible())
460     QTimer::singleShot(250, this, SLOT(slotProgressBarUpdate()));
461 }
462 
463 void AddFeedWizard::getUrlDone(int result, int feedId, QString feedUrlStr,
464                                QString error, QByteArray data,
465                                QDateTime dtReply, QString codecName)
466 {
467   if (!data.isEmpty()) {
468     bool isFeed = false;
469     QString errorStr;
470     int errorLine;
471     int errorColumn;
472     QDomDocument doc("parseDoc");
473     if (!doc.setContent(data, false, &errorStr, &errorLine, &errorColumn)) {
474       qWarning() << QString("Parse data error (1): url %1, id %2, line %3, column %4: %5").
475                     arg(feedUrlStr).arg(feedId).
476                     arg(errorLine).arg(errorColumn).arg(errorStr);
477     } else {
478       QDomElement docElem = doc.documentElement();
479       if ((docElem.tagName() == "rss") || (docElem.tagName() == "feed") ||
480           (docElem.tagName() == "rdf:RDF"))
481         isFeed = true;
482     }
483 
484     if (!isFeed) {
485       QString str = QString::fromUtf8(data);
486 
487       QzRegExp rx("<link[^>]+(atom|rss)\\+xml[^>]+>", Qt::CaseInsensitive);
488       int pos = rx.indexIn(str);
489       if (pos > -1) {
490         str = rx.cap(0);
491         rx.setPattern("href=\"([^\"]+)");
492         pos = rx.indexIn(str);
493         if (pos > -1) {
494           QString linkFeedString = rx.cap(1);
495           linkFeedString.replace("&amp;", "&", Qt::CaseInsensitive);
496           QUrl url(linkFeedString);
497           QUrl feedUrl(feedUrlStr);
498           if (url.host().isEmpty()) {
499             url.setScheme(feedUrl.scheme());
500             url.setHost(feedUrl.host());
501             if (feedUrl.toString().indexOf('?') > -1) {
502               str = feedUrl.path();
503               str = str.left(str.lastIndexOf('/')+1);
504               url.setPath(str+url.path());
505             }
506           }
507           linkFeedString = url.toString();
508           qDebug() << "Parse feed URL, valid:" << linkFeedString;
509 
510           QSqlQuery q;
511           int duplicateFoundId = -1;
512           q.prepare("SELECT id FROM feeds WHERE xmlUrl LIKE :xmlUrl");
513           q.bindValue(":xmlUrl", linkFeedString);
514           q.exec();
515           if (q.next()) duplicateFoundId = q.value(0).toInt();
516 
517           if (0 <= duplicateFoundId) {
518             if (feedUrlString_ != linkFeedString)
519               textWarning->setText(tr("Duplicate feed!"));
520             else
521               textWarning->setText(tr("Can't find feed URL!"));
522             warningWidget_->setVisible(true);
523 
524             deleteFeed();
525             progressBar_->hide();
526             page(0)->setEnabled(true);
527             selectedPage = false;
528             button(QWizard::CancelButton)->setEnabled(true);
529           } else {
530             feedUrlString_ = linkFeedString;
531             q.prepare("UPDATE feeds SET xmlUrl = :xmlUrl WHERE id == :id");
532             q.bindValue(":xmlUrl", linkFeedString);
533             q.bindValue(":id", feedId);
534             q.exec();
535 
536             authentication_->setChecked(false);
537 
538             emit signalRequestUrl(feedId, linkFeedString, QDateTime(), "");
539           }
540         }
541       }
542       if (pos < 0) {
543         textWarning->setText(tr("Can't find feed URL!"));
544         warningWidget_->setVisible(true);
545 
546         deleteFeed();
547         progressBar_->hide();
548         page(0)->setEnabled(true);
549         selectedPage = false;
550         button(QWizard::CancelButton)->setEnabled(true);
551       }
552       return;
553     }
554 
555     emit xmlReadyParse(data, feedId, dtReply, codecName);
556   }
557 
558   if ((result < 0) || data.isEmpty()) {
559     if ((result >= -5) && (result <= -1))
560       textWarning->setText(error);
561     else
562       textWarning->setText(tr("Request failed!"));
563     warningWidget_->setVisible(true);
564     qWarning() << QString("Request failed: result = %1, error - %2, url - %3").
565                   arg(result).arg(error).arg(feedUrlStr);
566 
567     deleteFeed();
568     progressBar_->hide();
569     page(0)->setEnabled(true);
570     selectedPage = false;
571     button(QWizard::CancelButton)->setEnabled(true);
572   }
573 }
574 
575 void AddFeedWizard::slotUpdateFeed(int feedId, bool, int newCount, QString)
576 {
577   qDebug() << "ParseDone: " << feedUrlString_;
578   selectedPage = true;
579   newCount_ = newCount;
580 
581   if (titleFeedAsName_->isChecked()) {
582     QSqlQuery q;
583     q.exec(QString("SELECT title FROM feeds WHERE id=='%1'").arg(feedId));
584     if (q.first()) nameFeedEdit_->setText(q.value(0).toString());
585     nameFeedEdit_->selectAll();
586     nameFeedEdit_->setFocus();
587   }
588   if (!finishOn) {
589     button(QWizard::CancelButton)->setEnabled(true);
590     next();
591   } else {
592     finish();
593   }
594   progressBar_->hide();
595 }
596 
597 void AddFeedWizard::finish()
598 {
599   QSqlQuery q;
600   q.exec(QString("SELECT htmlUrl FROM feeds WHERE id=='%1'").arg(feedId_));
601   if (q.first())
602     htmlUrlString_ = q.value(0).toString();
603 
604   feedParentId_ = foldersTree_->currentItem()->text(1).toInt();
605 
606   // Correct rowToParent field
607   QList<int> idList;
608   q.exec("SELECT id FROM feeds WHERE parentId=0 ORDER BY rowToParent");
609   while (q.next()) {
610     if (feedId_ != q.value(0).toInt())
611       idList << q.value(0).toInt();
612   }
613   for (int i = 0; i < idList.count(); i++) {
614     q.exec(QString("UPDATE feeds SET rowToParent='%1' WHERE id=='%2'").
615            arg(i).arg(idList.at(i)));
616   }
617 
618   // Calculate row number to insert feed
619   int rowToParent = 0;
620   q.exec(QString("SELECT count(id) FROM feeds WHERE parentId='%1' AND id!='%2'").
621          arg(feedParentId_).arg(feedId_));
622   if (q.next()) rowToParent = q.value(0).toInt();
623 
624   int auth = 0;
625   if (authentication_->isChecked()) auth = 1;
626 
627   q.prepare("UPDATE feeds SET text = ?, parentId = ?, rowToParent = ?, authentication = ? WHERE id == ?");
628   q.addBindValue(nameFeedEdit_->text());
629   q.addBindValue(feedParentId_);
630   q.addBindValue(rowToParent);
631   q.addBindValue(auth);
632   q.addBindValue(feedId_);
633   q.exec();
634 
635   if (auth) {
636     QUrl url(feedUrlString_);
637     QString server = url.host();
638 
639     q.prepare("SELECT * FROM passwords WHERE server=?");
640     q.addBindValue(server);
641     q.exec();
642     if (!q.next()) {
643       q.prepare("INSERT INTO passwords (server, username, password) "
644                 "VALUES (:server, :username, :password)");
645       q.bindValue(":server", server);
646       q.bindValue(":username", user_->text());
647       q.bindValue(":password", pass_->text().toUtf8().toBase64());
648       q.exec();
649     }
650   }
651 
652   if (feedParentId_) {
653     q.prepare("SELECT columns, sort, sortType, "
654               "updateIntervalEnable, updateInterval, updateIntervalType, "
655               "displayEmbeddedImages, displayNews, layoutDirection, "
656               "javaScriptEnable "
657               "FROM feeds WHERE id=?");
658     q.addBindValue(feedParentId_);
659     q.exec();
660     if (q.next()) {
661       QSqlQuery q1;
662       q1.prepare("UPDATE feeds SET columns = ?, sort = ?, sortType = ?, "
663                  "updateIntervalEnable = ?, updateInterval = ?, updateIntervalType = ?, "
664                  "displayEmbeddedImages = ?, displayNews = ?, layoutDirection = ?, "
665                  "javaScriptEnable = ? "
666                  "WHERE id == ?");
667       q1.addBindValue(q.value(0));
668       q1.addBindValue(q.value(1));
669       q1.addBindValue(q.value(2));
670       q1.addBindValue(q.value(3));
671       q1.addBindValue(q.value(4));
672       q1.addBindValue(q.value(5));
673       q1.addBindValue(q.value(6));
674       q1.addBindValue(q.value(7));
675       q1.addBindValue(q.value(8));
676       q1.addBindValue(q.value(9));
677       q1.addBindValue(feedId_);
678       q1.exec();
679     }
680   }
681 
682   accept();
683 }
684 
685 /*! \brief Adding new folder **************************************************/
686 void AddFeedWizard::newFolder()
687 {
688   AddFolderDialog *addFolderDialog = new AddFolderDialog(this);
689   QList<QTreeWidgetItem *> treeItems =
690       addFolderDialog->foldersTree_->findItems(foldersTree_->currentItem()->text(1),
691                                                Qt::MatchFixedString | Qt::MatchRecursive,
692                                                1);
693   addFolderDialog->foldersTree_->setCurrentItem(treeItems.at(0));
694 
695   if (addFolderDialog->exec() == QDialog::Rejected) {
696     delete addFolderDialog;
697     return;
698   }
699 
700   int folderId = 0;
701   QString folderText = addFolderDialog->nameFeedEdit_->text();
702   int parentId = addFolderDialog->foldersTree_->currentItem()->text(1).toInt();
703 
704   // Calculate row number to insert folder
705   int rowToParent = 0;
706   QSqlQuery q;
707   q.exec(QString("SELECT count(id) FROM feeds WHERE parentId='%1'").arg(parentId));
708   if (q.first())
709     rowToParent = q.value(0).toInt();
710 
711   // Add folder
712   q.prepare("INSERT INTO feeds(text, created, parentId, rowToParent) "
713             "VALUES (:text, :feedCreateTime, :parentId, :rowToParent)");
714   q.bindValue(":text", folderText);
715   q.bindValue(":feedCreateTime",
716               QLocale::c().toString(QDateTime::currentDateTimeUtc(), "yyyy-MM-ddTHH:mm:ss"));
717   q.bindValue(":parentId", parentId);
718   q.bindValue(":rowToParent", rowToParent);
719   q.exec();
720 
721   folderId = q.lastInsertId().toInt();
722   q.finish();
723 
724   treeItems = foldersTree_->findItems(QString::number(parentId),
725                                       Qt::MatchFixedString | Qt::MatchRecursive,
726                                       1);
727   QStringList treeItem;
728   treeItem << folderText << QString::number(folderId);
729   QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem(treeItem);
730   treeItems.at(0)->addChild(treeWidgetItem);
731   foldersTree_->setCurrentItem(treeWidgetItem);
732 
733   delete addFolderDialog;
734 }
735