summaryrefslogtreecommitdiff
path: root/src/sync/googlesynchandler.cpp
diff options
context:
space:
mode:
authorSiteshwar Vashisht <siteshwar@gmail.com>2012-04-23 01:18:01 +0200
committerAndrea Diamantini <adjam7@gmail.com>2012-04-23 01:46:11 +0200
commitcd6eee46a7405bc2365ada5ca7cd2daab01e06c0 (patch)
treef9c35fedba651e15fa2550804bcd9261c5aa1a91 /src/sync/googlesynchandler.cpp
parentFix opensearch add shortcut handling (diff)
downloadrekonq-cd6eee46a7405bc2365ada5ca7cd2daab01e06c0.tar.xz
Google Sync Bookmarks in rekonq!
- Added UI bits to configure Google Bookamrks Sync - Implemented sync, 2-way merging local and remote bk - Remember to not update local history while remotely adding bookmarks - Encode parameters of add bookmarks request while posting data. - (adjam) fix compile warnings This is the squashed commit of Situ's work on Google Bookmarks Sync SQUASHED BY: adjam ;) REVIEW: 104673 DIGEST: Google Bookmarks Sync REVIEWED BY: adjam
Diffstat (limited to 'src/sync/googlesynchandler.cpp')
-rw-r--r--src/sync/googlesynchandler.cpp437
1 files changed, 437 insertions, 0 deletions
diff --git a/src/sync/googlesynchandler.cpp b/src/sync/googlesynchandler.cpp
new file mode 100644
index 00000000..801b7b62
--- /dev/null
+++ b/src/sync/googlesynchandler.cpp
@@ -0,0 +1,437 @@
+/* ============================================================
+*
+* This file is a part of the rekonq project
+*
+* Copyright (C) 2012 by Siteshwar Vashisht <siteshwar at gmail dot com>
+* Copyright (C) 2011 by Andrea Diamantini <adjam7 at gmail dot com>
+*
+*
+* 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) version 3 or any later version
+* accepted by the membership of KDE e.V. (or its successor approved
+* by the membership of KDE e.V.), which shall act as a proxy
+* defined in Section 14 of version 3 of the license.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* ============================================================ */
+
+
+// Self Includes
+#include "googlesynchandler.h"
+#include "googlesynchandler.moc"
+
+// Auto Includes
+#include "rekonq.h"
+
+// Local Includes
+#include "application.h"
+#include "bookmarkmanager.h"
+
+// KDE Includes
+#include <KStandardDirs>
+#include <klocalizedstring.h>
+#include <kbookmarkmanager.h>
+
+#include <QList>
+#include <QWebPage>
+#include <QWebFrame>
+#include <QWebElement>
+#include <QUrl>
+#include <QWebSettings>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QDomDocument>
+
+
+GoogleSyncHandler::GoogleSyncHandler(QObject *parent)
+ : SyncHandler(parent)
+ , _mode(RECEIVE_CHANGES)
+ , _doLogin(false)
+ , _isSyncing(false)
+ , _reply(0)
+ , _requestCount(0)
+{
+ kDebug() << "Creating Google Bookmarks handler...";
+ _webPage.settings()->setAttribute(QWebSettings::AutoLoadImages, false);
+ _webPage.settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, true);
+ connect(&_webPage, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool)));
+}
+
+
+void GoogleSyncHandler::initialLoadAndCheck()
+{
+ if (!ReKonfig::syncEnabled())
+ {
+ _firstTimeSynced = false;
+ return;
+ }
+
+ // Bookmarks
+ if (ReKonfig::syncBookmarks())
+ {
+ _mode = RECEIVE_CHANGES;
+ startLogin();
+ }
+
+ if (ReKonfig::syncHistory())
+ {
+ emit syncStatus(Rekonq::History, false, i18n("Not supported!"));
+ }
+
+ if (ReKonfig::syncHistory())
+ {
+ emit syncStatus(Rekonq::Passwords, false, i18n("Not supported!"));
+ }
+}
+
+
+bool GoogleSyncHandler::syncRelativeEnabled(bool check)
+{
+ if (!ReKonfig::syncEnabled())
+ return false;
+
+ if (!_firstTimeSynced)
+ return false;
+
+ return check;
+}
+
+
+// ---------------------------------------------------------------------------------------
+
+
+void GoogleSyncHandler::syncHistory()
+{
+ kDebug() << "Syncing history not supported!";
+ emit syncStatus(Rekonq::History, false, i18n("Syncing history not supported!"));
+ emit syncHistoryFinished(false);
+}
+
+
+void GoogleSyncHandler::syncPasswords()
+{
+ kDebug() << "Syncing passwords not supported!";
+ emit syncStatus(Rekonq::Passwords, false, i18n("Syncing passwords not supported!"));
+ emit syncPasswordsFinished(false);
+}
+
+
+void GoogleSyncHandler::syncBookmarks()
+{
+
+ if (_isSyncing)
+ {
+ kDebug() << "Sync already in progress!";
+ return;
+ }
+ _mode = SEND_CHANGES;
+ startLogin();
+}
+
+void GoogleSyncHandler::startLogin()
+{
+ if (ReKonfig::syncUser().isEmpty() || ReKonfig::syncPass().isEmpty())
+ {
+ kDebug() << "No username or password!";
+ emit syncStatus(Rekonq::Bookmarks, false, i18n("No username or password!"));
+ emit syncBookmarksFinished(false);
+ return;
+ }
+
+ _isSyncing = true;
+
+ _doLogin = true;
+
+ kDebug() << "Loading login page...";
+ _webPage.mainFrame()->load(QUrl("http://bookmarks.google.com/"));
+}
+
+//Loading a webpage finished, what action to take is decided based on url we have loaded.
+void GoogleSyncHandler::loadFinished(bool ok)
+{
+ kDebug() << "Load Finished" << ok;
+ if (!ok)
+ {
+ kDebug() << "Error loading: " << _webPage.mainFrame()->url();
+ emit syncStatus(Rekonq::Bookmarks, false, i18n( "Error loading: " + _webPage.mainFrame()->url().toEncoded()));
+
+ _isSyncing = false;
+ return;
+ }
+
+ kDebug() << _webPage.mainFrame()->url();
+ kDebug() << "Path : " << _webPage.mainFrame()->url().path();
+
+ QString path = _webPage.mainFrame()->url().path();
+
+ if (path == "/ServiceLogin" && _doLogin == true)
+ {
+ // Let's login to our Google account
+ QWebFrame *frame = _webPage.mainFrame();
+
+ QWebElement email = frame->findFirstElement("#Email");
+ QWebElement passwd = frame->findFirstElement("#Passwd");
+ QWebElement form = frame->findFirstElement("#gaia_loginform");
+
+
+
+ email.setAttribute("value",ReKonfig::syncUser());
+ passwd.setAttribute("value",ReKonfig::syncPass());
+ form.evaluateJavaScript("this.submit();");
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Signing in..."));
+
+ // Login only once
+ _doLogin = false;
+ }
+ else if (path == "/bookmarks/")
+ {
+ //We get to this page after succesful login, let's fetch the bookmark list in Xml format.
+ QNetworkAccessManager *qnam = _webPage.networkAccessManager();
+ QNetworkRequest request;
+ request.setUrl(QUrl("http://www.google.com/bookmarks/?output=xml"));
+ _reply = qnam->get(request);
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Fetching bookmarks from server..."));
+ connect(_reply, SIGNAL(finished()), this, SLOT(fetchingBookmarksFinished()));
+ }
+ else if (path == "/ServiceLoginAuth")
+ {
+ emit syncStatus(Rekonq::Bookmarks, false, i18n("Login failed!"));
+ _isSyncing = false;
+ }
+ else if (path == "/bookmarks/mark")
+ {
+ QWebFrame *frame = _webPage.mainFrame();
+
+ QString sigKey = frame->findFirstElement("input[name=sig]").attribute("value");
+ kDebug() << "Signature Key is : " << sigKey;
+
+ QNetworkAccessManager *qnam = _webPage.networkAccessManager();
+
+ if (!_bookmarksToDelete.isEmpty())
+ {
+
+ for (QSet<QString>::const_iterator iter=_bookmarksToDelete.constBegin(); iter != _bookmarksToDelete.end(); ++iter)
+ {
+ QNetworkRequest request;
+ request.setUrl(QUrl("https://www.google.com/bookmarks/mark?dlq=" + *iter + "&sig=" + sigKey));
+
+ kDebug() << "Delete url is : " << request.url();
+ QNetworkReply *r = qnam->get(request);
+ connect(r, SIGNAL(finished()), this, SLOT(updateBookmarkFinished()));
+ ++_requestCount;
+ }
+ }
+
+ if (!_bookmarksToAdd.isEmpty())
+ {
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Adding bookmarks on server..."));
+ for (QSet<KUrl>::const_iterator iter=_bookmarksToAdd.constBegin(); iter != _bookmarksToAdd.end(); ++iter)
+ {
+ KBookmark bookmark = rApp->bookmarkManager()->bookmarkForUrl(*iter);
+ QByteArray postData;
+ postData.append("bkmk=" + QUrl::toPercentEncoding(bookmark.url().url().toUtf8()));
+ postData.append("&title=" + QUrl::toPercentEncoding(bookmark.text().toUtf8()));
+ postData.append("&annotation=");
+ postData.append("&labels=");
+ postData.append("&prev=/lookup");
+ postData.append("&sig=" + sigKey.toUtf8());
+
+ QNetworkRequest request;
+ request.setUrl(QUrl("https://www.google.com/bookmarks/mark?sig=" + sigKey +"&btnA"));
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+ kDebug() << "Url: " << request.url();
+ kDebug() << "Post data is :" << postData;
+ QNetworkReply *r = qnam->post(request, postData);
+ connect(r, SIGNAL(finished()), this, SLOT(updateBookmarkFinished()));
+ ++_requestCount;
+ }
+ }
+
+ _bookmarksToDelete.clear();
+ _bookmarksToAdd.clear();
+
+ }
+ else if (path == "/Logout")
+ {
+ //Session finished
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Done!"));
+ emit syncBookmarksFinished(true);
+ _isSyncing = false;
+ }
+ else
+ {
+ kDebug() << "Unknown Response!";
+ _isSyncing = false;
+ }
+
+}
+
+//We received bookmarks stored on server in xml format, now take the action based on which mode we are in.
+void GoogleSyncHandler::fetchingBookmarksFinished()
+{
+ QString data = _reply->readAll();
+
+ QDomDocument doc("bookmarks");
+ doc.setContent(data);
+
+ QDomNodeList bookmarksOnServer = doc.elementsByTagName("bookmark");
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Reading bookmarks..."));
+
+ BookmarkManager *manager = rApp->bookmarkManager();
+ KBookmarkGroup root = manager->rootGroup();
+
+
+ if (_mode == RECEIVE_CHANGES)
+ {
+
+ for (int i=0; i<bookmarksOnServer.size(); ++i)
+ {
+
+ QString title = getChildElement(bookmarksOnServer.at(i),"title");
+ QString url = getChildElement(bookmarksOnServer.at(i),"url");
+
+ KBookmark bookmark = manager->bookmarkForUrl(KUrl(url));
+ if (bookmark.isNull())
+ {
+ //Add bookmark
+ kDebug() << "Add bookmark";
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Adding bookmark "));
+ root.addBookmark(title.isEmpty() ? url : title, KUrl(url));
+ manager->manager()->emitChanged(root);
+ }
+
+ }
+
+ // After receiving changes, we compare local bookmarks with Google bookmarks and if some bookmarks exist locally but not on Google Bookmarks, we add them.
+ checkToAddGB(root, bookmarksOnServer);
+
+ if (!_bookmarksToAdd.isEmpty())
+ {
+ kDebug() << "Getting sigkey";
+ _webPage.mainFrame()->load(QUrl("https://www.google.com/bookmarks/mark?op=add&hl=en"));
+ }
+ else
+ {
+ _webPage.mainFrame()->load(QUrl("https://accounts.google.com/Logout?hl=en"));
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Signing out..."));
+ }
+ }
+ else
+ {
+ checkToAddGB(root, bookmarksOnServer);
+ checkToDeleteGB(manager, bookmarksOnServer);
+
+ if (!_bookmarksToAdd.isEmpty() || !_bookmarksToDelete.isEmpty())
+ {
+ kDebug() << "Getting sigkey";
+ _webPage.mainFrame()->load(QUrl("https://www.google.com/bookmarks/mark?op=add&hl=en"));
+ }
+ else
+ {
+ _webPage.mainFrame()->load(QUrl("https://accounts.google.com/Logout?hl=en"));
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Signing out..."));
+ }
+ }
+
+ _reply->deleteLater();
+}
+
+//Get value of a child element of a dom node
+QString GoogleSyncHandler::getChildElement(const QDomNode &node, QString name)
+{
+ QDomNodeList nodes = node.childNodes();
+
+ for (int j=0; j<nodes.size(); ++j)
+ {
+ QDomElement element = nodes.at(j).toElement();
+
+ if (nodes.at(j).nodeName() == name)
+ {
+ //kDebug() << "Url : " << element.text();
+ return element.text();
+ }
+ }
+ return NULL;
+}
+
+//This method checks whether we have any other bookmarks than the ones which exist on the server
+void GoogleSyncHandler::checkToAddGB(const KBookmarkGroup &root, const QDomNodeList &bookmarksOnServer)
+{
+ KBookmark current = root.first();
+
+ while (!current.isNull())
+ {
+ kDebug() << "Checking Url to add on Google Bookmarks: " << current.url();
+ bool found = false;
+ for (int i=0; i < bookmarksOnServer.count(); ++i)
+ {
+ if (current.isGroup())
+ {
+ kDebug() << "Checking group" << current.text();
+ checkToAddGB(current.toGroup(), bookmarksOnServer);
+ //skip adding a blank in _bookmarksToAdd
+ found = true;
+ break;
+ }
+ else if (current.url().url() == getChildElement(bookmarksOnServer.at(i),"url"))
+ {
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ kDebug() << "Adding to Google Bookmarks: " << current.url().url();
+ _bookmarksToAdd.insert(current.url());
+ }
+ current = root.next(current);
+ }
+}
+
+//Check whether we need to delete bookmarks while sending changes to Google Bookmarks
+void GoogleSyncHandler::checkToDeleteGB(BookmarkManager *manager, const QDomNodeList &bookmarksOnServer)
+{
+
+ for (int i=0; i < bookmarksOnServer.count(); ++i)
+ {
+ QString url = getChildElement(bookmarksOnServer.at(i),"url");
+
+ KBookmark result = manager->bookmarkForUrl(KUrl(url));
+ if (result.isNull())
+ {
+ kDebug() << "Deleting from Google Bookmarks: " << url;
+ _bookmarksToDelete.insert(getChildElement(bookmarksOnServer.at(i),"id"));
+ }
+ }
+
+}
+
+
+//Added or deleted a bookmark on server, check whether we succeed here, and logout when all requests are done!
+void GoogleSyncHandler::updateBookmarkFinished()
+{
+ --_requestCount;
+ QNetworkReply *reply = dynamic_cast<QNetworkReply*>(sender());
+ if (reply->error() != QNetworkReply::NoError)
+ kDebug() << "Network Error while adding bookmark to server, code is: " << reply->error();
+ else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302)
+ kDebug() << "Unexpected reply : " << reply->readAll();
+ else
+ kDebug() << "Success!";
+
+ if (_requestCount <= 0)
+ {
+ _webPage.mainFrame()->load(QUrl("https://accounts.google.com/Logout?hl=en"));
+ emit syncStatus(Rekonq::Bookmarks, true, i18n("Signing out..."));
+ }
+
+}