X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Fgooglereader.cpp;fp=src%2Fgooglereader.cpp;h=5919ca4b10f1eb08c9114f8accd91a98330d7210;hb=7f1f955b572af3ed84cf079fdd9cee4626db010a;hp=0000000000000000000000000000000000000000;hpb=7dce24304bbca9ff6639fa212183c6cc6c203a26;p=grr diff --git a/src/googlereader.cpp b/src/googlereader.cpp new file mode 100644 index 0000000..5919ca4 --- /dev/null +++ b/src/googlereader.cpp @@ -0,0 +1,552 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "googlereader.h" + +void Feed::updateSubscription(Feed *feed) { + title = feed->title; + sortid = feed->sortid; + firstitemmsec = feed->firstitemmsec; + cat_id = feed->cat_id; + cat_label = feed->cat_label; + subscription_updated = true; +} + +void Feed::fetch(bool cont) { + QNetworkRequest request; + QByteArray ba = "http://www.google.com/reader/atom/"; + + ba.append(QUrl::toPercentEncoding(id)); + QUrl url = QUrl::fromEncoded(ba); + + if(continuation != "" && cont) + url.addEncodedQueryItem("c", continuation.toUtf8()); + + request.setUrl(url); + reader->getManager()->get(request); +} + +GoogleReader::GoogleReader() { + connect(&manager, SIGNAL(finished(QNetworkReply*)), + SLOT(downloadFinished(QNetworkReply*))); + + SID = NULL; + updateSubscriptionsPending = false; + updateUnreadPending = false; + SIDPending = false; + + login_url.setUrl("https://www.google.com/accounts/ClientLogin"); + subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list"); + unread_url.setUrl("http://www.google.com/reader/api/0/unread-count"); + edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-"); + token_url.setUrl("http://www.google.com/reader/api/0/token"); + markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-"); + + /* Add the virtual 'Starred items' feed */ + Feed *feed = new Feed(this); + feed->id = "user/-/state/com.google/starred"; + feed->title = "Starred items"; + feed->special = true; + feeds.insert(feed->id, feed); + connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged())); +} + +void GoogleReader::downloadFinished(QNetworkReply *reply) { + QUrl url = reply->url(); + + /* TODO: Instead of comparing against the url, use the signal from the + * QNetworkReply... */ + + if (reply->error()) { + qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString()); + if(url == login_url) { + SIDPending = false; + emit loginFailed("Incorrect username or password"); + } + else if(url == edittag_url) + getToken(); + return; + } + else if(url == login_url) { + QByteArray data = reply->readAll(); + data.remove(0, data.indexOf("SID=", 0) + 4); + data.remove(data.indexOf("\n", 0), 1024); + SID = strdup(data.data()); + + qDebug() << "SID:" << SID; + + manager.cookieJar()->setCookiesFromUrl( + QList() << QNetworkCookie("SID", SID), + QUrl("http://www.google.com")); + + SIDPending = false; + + getToken(); + + /* TODO: Replace this with a proper state machine */ + if(updateSubscriptionsPending) { + updateSubscriptionsPending = false; + updateSubscriptions(); + } + } + else if(url == token_url) { + token = reply->readAll(); + qDebug() << "token:" << token; + } + else if(url == subscriptions_url) { + QByteArray data = reply->readAll(); + QDomDocument dom; + dom.setContent(data); + parseSubscriptions(dom); + emit updateSubscriptionsComplete(); + + /* TODO: Replace this with a proper state machine */ + if(updateUnreadPending) { + updateUnreadPending = false; + updateUnread(); + } + } + else if(url == unread_url) { + QByteArray data = reply->readAll(); + QDomDocument dom; + dom.setContent(data); + parseUnread(dom); + emit updateUnreadComplete(); + } + else if(url == edittag_url) { + QByteArray data = reply->readAll(); + //qDebug() << "Result:" << data; + } + else if(url == markallread_url) { + QByteArray data = reply->readAll(); + //qDebug() << "Result:" << data; + } + else { + QByteArray data = reply->readAll(); + QDomDocument dom; + dom.setContent(data); + parseFeed(dom); + } + + reply->deleteLater(); +} + +void GoogleReader::parseFeed(QDomDocument dom) { + QDomElement set, e; + QDomNode n, c; + QString continuation, feedsource; + Feed *feed = NULL; + + set = dom.firstChildElement(); + + for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) { + e = n.toElement(); + QString name = e.tagName(); + if(name == "entry") { + Entry entry; + QString content, summary; + + QString locked = e.attribute("gr:is-read-state-locked", "false"); + if(locked == "true") + entry.flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ; + + entry.crawled = e.attribute("gr:crawl-timestamp-msec", "0").toULongLong(); + + for(c = n.firstChild(); !c.isNull(); c = c.nextSibling()) { + e = c.toElement(); + name = e.tagName(); + if(name == "id") + entry.id = e.text(); + else if(name == "title") { + QWebPage p; + p.mainFrame()->setHtml(e.text()); + entry.title = p.mainFrame()->toPlainText(); + } + else if(name == "published") + entry.published = QDateTime::fromString(e.text(), "yyyy-MM-dd'T'HH:mm:ss'Z'"); + else if(name == "link") + entry.link = QUrl(e.attribute("href", "")); + else if(name == "source") + entry.source = e.attribute("gr:stream-id", ""); + else if(name == "content") + content = e.text(); + else if(name == "summary") + summary = e.text(); + else if(name == "author") { + e = c.firstChild().toElement(); + entry.author = e.text(); + if(entry.author == "(author unknown)") + entry.author = ""; + } + else if(name == "category") { + QString label = e.attribute("label", ""); + if(label == "read") + entry.flags |= ENTRY_FLAG_READ; + else if(label == "starred") + entry.flags |= ENTRY_FLAG_STARRED; + } + } + + if(content != "") + entry.content = content; + else if(summary != "") + entry.content = summary; + + if(!feed) + feed = feeds.value(feedsource == "" ? entry.source : feedsource); + + if(feed) { + entry.feed = feed; + feed->addEntry(new Entry(entry)); + } + } + else if(name == "gr:continuation") { + continuation = e.text(); + } + else if(name == "id") { + if(e.text().endsWith("/state/com.google/starred")) + feedsource = "user/-/state/com.google/starred"; + } + } + + if(feed) { + feed->lastUpdated = QDateTime::currentDateTime(); + feed->continuation = continuation; + feed->signalUpdated(); + } +} + +void GoogleReader::parseSubscriptions(QDomDocument dom) { + QDomElement set, e; + QDomNode n, c; + + /* Clear the subscription updated flag */ + QHash::iterator i; + for(i = feeds.begin(); i != feeds.end(); ++i) + i.value()->subscription_updated = false; + + set = dom.firstChildElement(); + set = set.firstChildElement("list"); + set = set.firstChildElement("object"); + + for (; !set.isNull(); set = set.nextSiblingElement("object")) { + Feed *feed = new Feed(this); + Feed *existing_feed; + + for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) { + e = n.toElement(); + QString name = e.attribute("name"); + if(name == "id") + feed->id = e.text(); + else if(name == "title") + feed->title = e.text(); + else if(name == "sortid") + feed->sortid = e.text(); + else if(name == "firstitemmsec") + feed->firstitemmsec = e.text(); + else if(name == "categories") { + for(c = n.firstChild().firstChild(); !c.isNull(); c = c.nextSibling()) { + e = c.toElement(); + QString name = e.attribute("name"); + if(name == "id") + feed->cat_id = e.text(); + else if(name == "label") + feed->cat_label = e.text(); + } + } + } + + existing_feed = feeds.value(feed->id); + if(existing_feed) { + existing_feed->updateSubscription(feed); + delete(feed); + feed = existing_feed; + + } + else { + feed->subscription_updated = true; + feeds.insert(feed->id, feed); + connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged())); + } + } + + /* Delete feeds no longer subscribed to */ + for(i = feeds.begin(); i != feeds.end(); ++i) { + if(i.value()->subscription_updated == false && i.value()->special == false) { + printf("DELETED: %s\n", i.value()->title.toLatin1().data()); + i = feeds.erase(i); + } + } + + lastUpdated = QDateTime::currentDateTime(); +} + +void GoogleReader::parseUnread(QDomDocument dom) { + QDomElement set, e; + QDomNode n, c; + + set = dom.firstChildElement(); + set = set.firstChildElement("list"); + set = set.firstChildElement("object"); + + for (; !set.isNull(); set = set.nextSiblingElement("object")) { + QString id; + int count = 0; + ulong newestitem = 0; + + for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) { + e = n.toElement(); + QString name = e.attribute("name"); + if(name == "id") + id = e.text(); + else if(name == "count") + count = e.text().toInt(); + else if(name == "newestItemTimestampUsec") + newestitem = e.text().toULong(); + } + + Feed *f = feeds.value(id); + if(f) { + f->unread = count; + f->newestitem = newestitem; + //qDebug() << f->title << "->" << count; + } + else { + //printf("%s not found\n", id.toLatin1().data()); + } + } + + lastUpdated = QDateTime::currentDateTime(); +} + +void GoogleReader::getSID() { + + if(SIDPending) + return; + + SIDPending = true; + + QNetworkRequest request; + request.setUrl(login_url); + + buffer.open(QBuffer::ReadWrite | QBuffer::Truncate); + buffer.write("Email="); + buffer.write(QUrl::toPercentEncoding(login)); + buffer.write("&Passwd="); + buffer.write(QUrl::toPercentEncoding(passwd)); + buffer.write("&service=reader&source=grms&continue=http://www.google.com"); + + //buffer.seek(0); + //qDebug() << buffer.readAll(); + + buffer.seek(0); + manager.post(request, &buffer); +} + +void GoogleReader::getToken() { + QNetworkRequest request; + request.setUrl(token_url); + manager.get(request); +} + +void GoogleReader::updateSubscriptions() { + QNetworkRequest request; + + if(updateSubscriptionsPending) + return; + + if(!SID) { + updateSubscriptionsPending = true; + getSID(); + return; + } + + request.setUrl(subscriptions_url); + manager.get(request); +} + +void GoogleReader::updateUnread() { + QNetworkRequest request; + + if(updateUnreadPending) + return; + + if(!SID) { + updateUnreadPending = true; + getSID(); + return; + } + + request.setUrl(unread_url); + manager.get(request); +} + +static bool compareFeedItems(const Feed *f1, const Feed *f2) { + if(f1->special && !f2->special) + return true; + + if(f2->special && !f1->special) + return false; + + if(f1->cat_label == f2->cat_label) + return f1->title.toLower() < f2->title.toLower(); + + if(f1->cat_label.length() == 0) + return false; + + if(f2->cat_label.length() == 0) + return true; + + return f1->cat_label.toLower() < f2->cat_label.toLower(); +} + +QList GoogleReader::getFeeds() { + QList list = feeds.values(); + qSort(list.begin(), list.end(), compareFeedItems); + return list; +} + +void Feed::addEntry(Entry *entry) { + entries.insert(entry->id, entry); +} + +void Feed::delEntry(Entry *entry) { + entries.remove(entry->id); +} + +void Feed::signalUpdated() { + // TODO: Clean this up + emit updateFeedComplete(); +} + +void Feed::updateUnread(int i) { + bool allRead = (unread == 0); + + unread += i; + if(unread <= 0) unread = 0; + + if(allRead != (unread == 0)) + emit allReadChanged(); +} + +void Feed::markRead() { + if(unread > 0) { + /* Mark all the remaining items read */ + + QNetworkRequest request; + request.setUrl(reader->markallread_url); + + buffer.open(QBuffer::ReadWrite | QBuffer::Truncate); + buffer.write("s="); + buffer.write(QUrl::toPercentEncoding(id)); + //buffer.write("&ts="); + //buffer.write(QByteArray::number(oldest)); + buffer.write("&T="); + buffer.write(QUrl::toPercentEncoding(reader->token)); + + //buffer.seek(0); + //qDebug() << buffer.readAll(); + + buffer.seek(0); + reader->manager.post(request, &buffer); + + unread = 0; + + /* Go over all the entries and mark them read */ + QHash::iterator i; + for(i = entries.begin(); i != entries.end(); ++i) + i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED; + } + + emit allReadChanged(); +} + +static bool compareEntryItems(const Entry *e1, const Entry *e2) { + return e1->published > e2->published; +} + +QList Feed::getEntries() { + QList list = entries.values(); + qSort(list.begin(), list.end(), compareEntryItems); + return list; +} + +/* TODO: Remove the duplicate code in changing stated */ + +void Entry::markRead(bool mark_read) { + /* Check if the read flag differs from the requested state */ + if(((flags & ENTRY_FLAG_READ) != 0) == mark_read) + return; + + /* Cannot mark an item unread if it's locked */ + if((flags & ENTRY_FLAG_LOCKED) && !mark_read) + return; + + QNetworkRequest request; + request.setUrl(feed->reader->edittag_url); + + postread.open(QBuffer::ReadWrite | QBuffer::Truncate); + postread.write("i="); + postread.write(QUrl::toPercentEncoding(id)); + if(mark_read) + postread.write("&a="); + else + postread.write("&r="); + postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read")); + postread.write("&ac=edit-tags&T="); + postread.write(QUrl::toPercentEncoding(feed->reader->token)); + postread.seek(0); + feed->reader->manager.post(request, &postread); + + feed->updateUnread(mark_read ? -1 : 1); + + if(mark_read) + flags |= ENTRY_FLAG_READ; + else + flags &= ~ENTRY_FLAG_READ; +} + +void Entry::markStar(bool mark_star) { + /* Check if the starred flag differs from the requested state */ + if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star) + return; + + QNetworkRequest request; + request.setUrl(feed->reader->edittag_url); + + poststar.open(QBuffer::ReadWrite | QBuffer::Truncate); + poststar.write("i="); + poststar.write(QUrl::toPercentEncoding(id)); + if(mark_star) + poststar.write("&a="); + else + poststar.write("&r="); + poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred")); + poststar.write("&ac=edit-tags&T="); + poststar.write(QUrl::toPercentEncoding(feed->reader->token)); + poststar.seek(0); + feed->reader->manager.post(request, &poststar); + + Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred"); + + if(mark_star) { + starred->addEntry(this); + flags |= ENTRY_FLAG_STARRED; + } + else { + starred->delEntry(this); + flags &= ~ENTRY_FLAG_STARRED; + } +}