2 #include <QApplication>
3 #include <QNetworkRequest>
4 #include <QNetworkReply>
5 #include <QNetworkCookie>
9 #include <QDomDocument>
16 #include "googlereader.h"
18 void Feed::updateSubscription(Feed *feed) {
20 sortid = feed->sortid;
21 firstitemmsec = feed->firstitemmsec;
22 cat_id = feed->cat_id;
23 cat_label = feed->cat_label;
24 subscription_updated = true;
27 void Feed::fetch(bool cont) {
28 QNetworkRequest request;
29 QByteArray ba = "http://www.google.com/reader/atom/";
31 ba.append(QUrl::toPercentEncoding(id));
32 QUrl url = QUrl::fromEncoded(ba);
34 if(continuation != "" && cont)
35 url.addEncodedQueryItem("c", continuation.toUtf8());
38 reader->getManager()->get(request);
41 GoogleReader::GoogleReader() {
42 connect(&manager, SIGNAL(finished(QNetworkReply*)),
43 SLOT(downloadFinished(QNetworkReply*)));
46 updateSubscriptionsPending = false;
47 updateUnreadPending = false;
50 login_url.setUrl("https://www.google.com/accounts/ClientLogin");
51 subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list");
52 unread_url.setUrl("http://www.google.com/reader/api/0/unread-count");
53 edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-");
54 token_url.setUrl("http://www.google.com/reader/api/0/token");
55 markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-");
57 /* Add the virtual 'Starred items' feed */
58 Feed *feed = new Feed(this);
59 feed->id = "user/-/state/com.google/starred";
60 feed->title = "Starred items";
62 feeds.insert(feed->id, feed);
63 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
66 void GoogleReader::downloadFinished(QNetworkReply *reply) {
67 QUrl url = reply->url();
69 /* TODO: Instead of comparing against the url, use the signal from the
73 qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString());
74 if(url == login_url) {
76 emit loginFailed("Incorrect username or password");
78 else if(url == edittag_url)
82 else if(url == login_url) {
83 QByteArray data = reply->readAll();
84 data.remove(0, data.indexOf("SID=", 0) + 4);
85 data.remove(data.indexOf("\n", 0), 1024);
86 SID = strdup(data.data());
88 qDebug() << "SID:" << SID;
90 manager.cookieJar()->setCookiesFromUrl(
91 QList<QNetworkCookie>() << QNetworkCookie("SID", SID),
92 QUrl("http://www.google.com"));
98 /* TODO: Replace this with a proper state machine */
99 if(updateSubscriptionsPending) {
100 updateSubscriptionsPending = false;
101 updateSubscriptions();
104 else if(url == token_url) {
105 token = reply->readAll();
106 qDebug() << "token:" << token;
108 else if(url == subscriptions_url) {
109 QByteArray data = reply->readAll();
111 dom.setContent(data);
112 parseSubscriptions(dom);
113 emit updateSubscriptionsComplete();
115 /* TODO: Replace this with a proper state machine */
116 if(updateUnreadPending) {
117 updateUnreadPending = false;
121 else if(url == unread_url) {
122 QByteArray data = reply->readAll();
124 dom.setContent(data);
126 emit updateUnreadComplete();
128 else if(url == edittag_url) {
129 QByteArray data = reply->readAll();
130 //qDebug() << "Result:" << data;
132 else if(url == markallread_url) {
133 QByteArray data = reply->readAll();
134 //qDebug() << "Result:" << data;
137 QByteArray data = reply->readAll();
139 dom.setContent(data);
143 reply->deleteLater();
146 void GoogleReader::parseFeed(QDomDocument dom) {
149 QString continuation, feedsource;
152 set = dom.firstChildElement();
154 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
156 QString name = e.tagName();
157 if(name == "entry") {
159 QString content, summary;
161 QString locked = e.attribute("gr:is-read-state-locked", "false");
163 entry.flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
165 entry.crawled = e.attribute("gr:crawl-timestamp-msec", "0").toULongLong();
167 for(c = n.firstChild(); !c.isNull(); c = c.nextSibling()) {
172 else if(name == "title") {
174 p.mainFrame()->setHtml(e.text());
175 entry.title = p.mainFrame()->toPlainText();
177 else if(name == "published")
178 entry.published = QDateTime::fromString(e.text(), "yyyy-MM-dd'T'HH:mm:ss'Z'");
179 else if(name == "link")
180 entry.link = QUrl(e.attribute("href", ""));
181 else if(name == "source")
182 entry.source = e.attribute("gr:stream-id", "");
183 else if(name == "content")
185 else if(name == "summary")
187 else if(name == "author") {
188 e = c.firstChild().toElement();
189 entry.author = e.text();
190 if(entry.author == "(author unknown)")
193 else if(name == "category") {
194 QString label = e.attribute("label", "");
196 entry.flags |= ENTRY_FLAG_READ;
197 else if(label == "starred")
198 entry.flags |= ENTRY_FLAG_STARRED;
203 entry.content = content;
204 else if(summary != "")
205 entry.content = summary;
208 feed = feeds.value(feedsource == "" ? entry.source : feedsource);
212 feed->addEntry(new Entry(entry));
215 else if(name == "gr:continuation") {
216 continuation = e.text();
218 else if(name == "id") {
219 if(e.text().endsWith("/state/com.google/starred"))
220 feedsource = "user/-/state/com.google/starred";
225 feed->lastUpdated = QDateTime::currentDateTime();
226 feed->continuation = continuation;
227 feed->signalUpdated();
231 void GoogleReader::parseSubscriptions(QDomDocument dom) {
235 /* Clear the subscription updated flag */
236 QHash<QString, Feed *>::iterator i;
237 for(i = feeds.begin(); i != feeds.end(); ++i)
238 i.value()->subscription_updated = false;
240 set = dom.firstChildElement();
241 set = set.firstChildElement("list");
242 set = set.firstChildElement("object");
244 for (; !set.isNull(); set = set.nextSiblingElement("object")) {
245 Feed *feed = new Feed(this);
248 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
250 QString name = e.attribute("name");
253 else if(name == "title")
254 feed->title = e.text();
255 else if(name == "sortid")
256 feed->sortid = e.text();
257 else if(name == "firstitemmsec")
258 feed->firstitemmsec = e.text();
259 else if(name == "categories") {
260 for(c = n.firstChild().firstChild(); !c.isNull(); c = c.nextSibling()) {
262 QString name = e.attribute("name");
264 feed->cat_id = e.text();
265 else if(name == "label")
266 feed->cat_label = e.text();
271 existing_feed = feeds.value(feed->id);
273 existing_feed->updateSubscription(feed);
275 feed = existing_feed;
279 feed->subscription_updated = true;
280 feeds.insert(feed->id, feed);
281 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
285 /* Delete feeds no longer subscribed to */
286 for(i = feeds.begin(); i != feeds.end(); ++i) {
287 if(i.value()->subscription_updated == false && i.value()->special == false) {
288 printf("DELETED: %s\n", i.value()->title.toLatin1().data());
293 lastUpdated = QDateTime::currentDateTime();
296 void GoogleReader::parseUnread(QDomDocument dom) {
300 set = dom.firstChildElement();
301 set = set.firstChildElement("list");
302 set = set.firstChildElement("object");
304 for (; !set.isNull(); set = set.nextSiblingElement("object")) {
307 ulong newestitem = 0;
309 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
311 QString name = e.attribute("name");
314 else if(name == "count")
315 count = e.text().toInt();
316 else if(name == "newestItemTimestampUsec")
317 newestitem = e.text().toULong();
320 Feed *f = feeds.value(id);
323 f->newestitem = newestitem;
324 //qDebug() << f->title << "->" << count;
327 //printf("%s not found\n", id.toLatin1().data());
331 lastUpdated = QDateTime::currentDateTime();
334 void GoogleReader::getSID() {
341 QNetworkRequest request;
342 request.setUrl(login_url);
344 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
345 buffer.write("Email=");
346 buffer.write(QUrl::toPercentEncoding(login));
347 buffer.write("&Passwd=");
348 buffer.write(QUrl::toPercentEncoding(passwd));
349 buffer.write("&service=reader&source=grms&continue=http://www.google.com");
352 //qDebug() << buffer.readAll();
355 manager.post(request, &buffer);
358 void GoogleReader::getToken() {
359 QNetworkRequest request;
360 request.setUrl(token_url);
361 manager.get(request);
364 void GoogleReader::updateSubscriptions() {
365 QNetworkRequest request;
367 if(updateSubscriptionsPending)
371 updateSubscriptionsPending = true;
376 request.setUrl(subscriptions_url);
377 manager.get(request);
380 void GoogleReader::updateUnread() {
381 QNetworkRequest request;
383 if(updateUnreadPending)
387 updateUnreadPending = true;
392 request.setUrl(unread_url);
393 manager.get(request);
396 static bool compareFeedItems(const Feed *f1, const Feed *f2) {
397 if(f1->special && !f2->special)
400 if(f2->special && !f1->special)
403 if(f1->cat_label == f2->cat_label)
404 return f1->title.toLower() < f2->title.toLower();
406 if(f1->cat_label.length() == 0)
409 if(f2->cat_label.length() == 0)
412 return f1->cat_label.toLower() < f2->cat_label.toLower();
415 QList<Feed *> GoogleReader::getFeeds() {
416 QList<Feed *> list = feeds.values();
417 qSort(list.begin(), list.end(), compareFeedItems);
421 void Feed::addEntry(Entry *entry) {
422 entries.insert(entry->id, entry);
425 void Feed::delEntry(Entry *entry) {
426 entries.remove(entry->id);
429 void Feed::signalUpdated() {
430 // TODO: Clean this up
431 emit updateFeedComplete();
434 void Feed::updateUnread(int i) {
435 bool allRead = (unread == 0);
438 if(unread <= 0) unread = 0;
440 if(allRead != (unread == 0))
441 emit allReadChanged();
444 void Feed::markRead() {
446 /* Mark all the remaining items read */
448 QNetworkRequest request;
449 request.setUrl(reader->markallread_url);
451 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
453 buffer.write(QUrl::toPercentEncoding(id));
454 //buffer.write("&ts=");
455 //buffer.write(QByteArray::number(oldest));
457 buffer.write(QUrl::toPercentEncoding(reader->token));
460 //qDebug() << buffer.readAll();
463 reader->manager.post(request, &buffer);
467 /* Go over all the entries and mark them read */
468 QHash<QString, Entry *>::iterator i;
469 for(i = entries.begin(); i != entries.end(); ++i)
470 i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED;
473 emit allReadChanged();
476 static bool compareEntryItems(const Entry *e1, const Entry *e2) {
477 return e1->published > e2->published;
480 QList<Entry *> Feed::getEntries() {
481 QList<Entry *> list = entries.values();
482 qSort(list.begin(), list.end(), compareEntryItems);
486 /* TODO: Remove the duplicate code in changing stated */
488 void Entry::markRead(bool mark_read) {
489 /* Check if the read flag differs from the requested state */
490 if(((flags & ENTRY_FLAG_READ) != 0) == mark_read)
493 /* Cannot mark an item unread if it's locked */
494 if((flags & ENTRY_FLAG_LOCKED) && !mark_read)
497 QNetworkRequest request;
498 request.setUrl(feed->reader->edittag_url);
500 postread.open(QBuffer::ReadWrite | QBuffer::Truncate);
501 postread.write("i=");
502 postread.write(QUrl::toPercentEncoding(id));
504 postread.write("&a=");
506 postread.write("&r=");
507 postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read"));
508 postread.write("&ac=edit-tags&T=");
509 postread.write(QUrl::toPercentEncoding(feed->reader->token));
511 feed->reader->manager.post(request, &postread);
513 feed->updateUnread(mark_read ? -1 : 1);
516 flags |= ENTRY_FLAG_READ;
518 flags &= ~ENTRY_FLAG_READ;
521 void Entry::markStar(bool mark_star) {
522 /* Check if the starred flag differs from the requested state */
523 if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star)
526 QNetworkRequest request;
527 request.setUrl(feed->reader->edittag_url);
529 poststar.open(QBuffer::ReadWrite | QBuffer::Truncate);
530 poststar.write("i=");
531 poststar.write(QUrl::toPercentEncoding(id));
533 poststar.write("&a=");
535 poststar.write("&r=");
536 poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred"));
537 poststar.write("&ac=edit-tags&T=");
538 poststar.write(QUrl::toPercentEncoding(feed->reader->token));
540 feed->reader->manager.post(request, &poststar);
542 Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred");
545 starred->addEntry(this);
546 flags |= ENTRY_FLAG_STARRED;
549 starred->delEntry(this);
550 flags &= ~ENTRY_FLAG_STARRED;