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()));
65 /* Add the virtual 'Shared items' feed */
66 feed = new Feed(this);
67 feed->id = "user/-/state/com.google/broadcast";
68 feed->title = "Shared items";
70 feeds.insert(feed->id, feed);
71 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
74 void GoogleReader::downloadFinished(QNetworkReply *reply) {
75 QUrl url = reply->url();
77 /* TODO: Instead of comparing against the url, use the signal from the
81 qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString());
82 if(url == login_url) {
84 emit loginFailed("Incorrect username or password");
86 else if(url == edittag_url)
90 else if(url == login_url) {
91 QByteArray data = reply->readAll();
92 data.remove(0, data.indexOf("SID=", 0) + 4);
93 data.remove(data.indexOf("\n", 0), 1024);
94 SID = strdup(data.data());
96 qDebug() << "SID:" << SID;
98 manager.cookieJar()->setCookiesFromUrl(
99 QList<QNetworkCookie>() << QNetworkCookie("SID", SID),
100 QUrl("http://www.google.com"));
106 /* TODO: Replace this with a proper state machine */
107 if(updateSubscriptionsPending) {
108 updateSubscriptionsPending = false;
109 updateSubscriptions();
112 else if(url == token_url) {
113 token = reply->readAll();
114 qDebug() << "token:" << token;
116 else if(url == subscriptions_url) {
117 QByteArray data = reply->readAll();
119 dom.setContent(data);
120 parseSubscriptions(dom);
121 emit updateSubscriptionsComplete();
123 /* TODO: Replace this with a proper state machine */
124 if(updateUnreadPending) {
125 updateUnreadPending = false;
129 else if(url == unread_url) {
130 QByteArray data = reply->readAll();
132 dom.setContent(data);
134 emit updateUnreadComplete();
136 else if(url == edittag_url) {
137 QByteArray data = reply->readAll();
138 //qDebug() << "Result:" << data;
140 else if(url == markallread_url) {
141 QByteArray data = reply->readAll();
142 //qDebug() << "Result:" << data;
145 QByteArray data = reply->readAll();
147 dom.setContent(data);
151 reply->deleteLater();
154 void GoogleReader::parseFeed(QDomDocument dom) {
157 QString continuation, feedsource;
160 set = dom.firstChildElement();
162 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
164 QString name = e.tagName();
165 if(name == "entry") {
167 QString content, summary;
169 QString locked = e.attribute("gr:is-read-state-locked", "false");
171 entry.flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
173 entry.crawled = e.attribute("gr:crawl-timestamp-msec", "0").toULongLong();
175 for(c = n.firstChild(); !c.isNull(); c = c.nextSibling()) {
180 else if(name == "title") {
182 p.mainFrame()->setHtml(e.text());
183 entry.title = p.mainFrame()->toPlainText();
185 else if(name == "published")
186 entry.published = QDateTime::fromString(e.text(), "yyyy-MM-dd'T'HH:mm:ss'Z'");
187 else if(name == "link")
188 entry.link = QUrl(e.attribute("href", ""));
189 else if(name == "source")
190 entry.source = e.attribute("gr:stream-id", "");
191 else if(name == "content")
193 else if(name == "summary")
195 else if(name == "author") {
196 e = c.firstChild().toElement();
197 entry.author = e.text();
198 if(entry.author == "(author unknown)")
201 else if(name == "category") {
202 QString label = e.attribute("label", "");
204 entry.flags |= ENTRY_FLAG_READ;
205 else if(label == "starred")
206 entry.flags |= ENTRY_FLAG_STARRED;
207 else if(label == "broadcast")
208 entry.flags |= ENTRY_FLAG_SHARED;
213 entry.content = content;
214 else if(summary != "")
215 entry.content = summary;
218 feed = feeds.value(feedsource == "" ? entry.source : feedsource);
222 feed->addEntry(new Entry(entry));
225 else if(name == "gr:continuation") {
226 continuation = e.text();
228 else if(name == "id") {
229 if(e.text().endsWith("/state/com.google/starred"))
230 feedsource = "user/-/state/com.google/starred";
231 else if(e.text().endsWith("/state/com.google/broadcast"))
232 feedsource = "user/-/state/com.google/broadcast";
237 feed->lastUpdated = QDateTime::currentDateTime();
238 feed->continuation = continuation;
239 feed->signalUpdated();
243 void GoogleReader::parseSubscriptions(QDomDocument dom) {
247 /* Clear the subscription updated flag */
248 QHash<QString, Feed *>::iterator i;
249 for(i = feeds.begin(); i != feeds.end(); ++i)
250 i.value()->subscription_updated = false;
252 set = dom.firstChildElement();
253 set = set.firstChildElement("list");
254 set = set.firstChildElement("object");
256 for (; !set.isNull(); set = set.nextSiblingElement("object")) {
257 Feed *feed = new Feed(this);
260 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
262 QString name = e.attribute("name");
265 else if(name == "title")
266 feed->title = e.text();
267 else if(name == "sortid")
268 feed->sortid = e.text();
269 else if(name == "firstitemmsec")
270 feed->firstitemmsec = e.text();
271 else if(name == "categories") {
272 for(c = n.firstChild().firstChild(); !c.isNull(); c = c.nextSibling()) {
274 QString name = e.attribute("name");
276 feed->cat_id = e.text();
277 else if(name == "label")
278 feed->cat_label = e.text();
283 existing_feed = feeds.value(feed->id);
285 existing_feed->updateSubscription(feed);
287 feed = existing_feed;
291 feed->subscription_updated = true;
292 feeds.insert(feed->id, feed);
293 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
297 /* Delete feeds no longer subscribed to */
298 for(i = feeds.begin(); i != feeds.end(); ++i) {
299 if(i.value()->subscription_updated == false && i.value()->special == 0) {
300 printf("DELETED: %s\n", i.value()->title.toLatin1().data());
305 lastUpdated = QDateTime::currentDateTime();
308 void GoogleReader::parseUnread(QDomDocument dom) {
312 set = dom.firstChildElement();
313 set = set.firstChildElement("list");
314 set = set.firstChildElement("object");
316 for (; !set.isNull(); set = set.nextSiblingElement("object")) {
319 ulong newestitem = 0;
321 for(n = set.firstChild(); !n.isNull(); n = n.nextSibling()) {
323 QString name = e.attribute("name");
326 else if(name == "count")
327 count = e.text().toInt();
328 else if(name == "newestItemTimestampUsec")
329 newestitem = e.text().toULong();
332 Feed *f = feeds.value(id);
335 f->newestitem = newestitem;
336 //qDebug() << f->title << "->" << count;
339 //printf("%s not found\n", id.toLatin1().data());
343 lastUpdated = QDateTime::currentDateTime();
346 void GoogleReader::getSID() {
353 QNetworkRequest request;
354 request.setUrl(login_url);
356 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
357 buffer.write("Email=");
358 buffer.write(QUrl::toPercentEncoding(login));
359 buffer.write("&Passwd=");
360 buffer.write(QUrl::toPercentEncoding(passwd));
361 buffer.write("&service=reader&source=grms&continue=http://www.google.com");
364 //qDebug() << buffer.readAll();
367 manager.post(request, &buffer);
370 void GoogleReader::getToken() {
371 QNetworkRequest request;
372 request.setUrl(token_url);
373 manager.get(request);
376 void GoogleReader::updateSubscriptions() {
377 QNetworkRequest request;
379 if(updateSubscriptionsPending)
383 updateSubscriptionsPending = true;
388 request.setUrl(subscriptions_url);
389 manager.get(request);
392 void GoogleReader::updateUnread() {
393 QNetworkRequest request;
395 if(updateUnreadPending)
399 updateUnreadPending = true;
404 request.setUrl(unread_url);
405 manager.get(request);
408 static bool compareFeedItems(const Feed *f1, const Feed *f2) {
409 if(f1->special || f2->special)
410 return f1->special > f2->special;
412 if(f1->cat_label == f2->cat_label)
413 return f1->title.toLower() < f2->title.toLower();
415 if(f1->cat_label.length() == 0)
418 if(f2->cat_label.length() == 0)
421 return f1->cat_label.toLower() < f2->cat_label.toLower();
424 QList<Feed *> GoogleReader::getFeeds() {
425 QList<Feed *> list = feeds.values();
426 qSort(list.begin(), list.end(), compareFeedItems);
430 void Feed::addEntry(Entry *entry) {
431 entries.insert(entry->id, entry);
434 void Feed::delEntry(Entry *entry) {
435 entries.remove(entry->id);
438 void Feed::signalUpdated() {
439 // TODO: Clean this up
440 emit updateFeedComplete();
443 void Feed::updateUnread(int i) {
444 bool allRead = (unread == 0);
447 if(unread <= 0) unread = 0;
449 if(allRead != (unread == 0))
450 emit allReadChanged();
453 void Feed::markRead() {
455 /* Mark all the remaining items read */
457 QNetworkRequest request;
458 request.setUrl(reader->markallread_url);
460 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
462 buffer.write(QUrl::toPercentEncoding(id));
463 //buffer.write("&ts=");
464 //buffer.write(QByteArray::number(oldest));
466 buffer.write(QUrl::toPercentEncoding(reader->token));
469 //qDebug() << buffer.readAll();
472 reader->manager.post(request, &buffer);
476 /* Go over all the entries and mark them read */
477 QHash<QString, Entry *>::iterator i;
478 for(i = entries.begin(); i != entries.end(); ++i)
479 i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED;
482 emit allReadChanged();
485 static bool compareEntryItems(const Entry *e1, const Entry *e2) {
486 return e1->published > e2->published;
489 QList<Entry *> Feed::getEntries() {
490 QList<Entry *> list = entries.values();
491 qSort(list.begin(), list.end(), compareEntryItems);
495 /* TODO: Remove the duplicate code in changing stated */
497 void Entry::markRead(bool mark_read) {
498 /* Check if the read flag differs from the requested state */
499 if(((flags & ENTRY_FLAG_READ) != 0) == mark_read)
502 /* Cannot mark an item unread if it's locked */
503 if((flags & ENTRY_FLAG_LOCKED) && !mark_read)
506 QNetworkRequest request;
507 request.setUrl(feed->reader->edittag_url);
509 postread.open(QBuffer::ReadWrite | QBuffer::Truncate);
510 postread.write("i=");
511 postread.write(QUrl::toPercentEncoding(id));
513 postread.write("&a=");
515 postread.write("&r=");
516 postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read"));
517 postread.write("&ac=edit-tags&T=");
518 postread.write(QUrl::toPercentEncoding(feed->reader->token));
520 feed->reader->manager.post(request, &postread);
522 feed->updateUnread(mark_read ? -1 : 1);
525 flags |= ENTRY_FLAG_READ;
527 flags &= ~ENTRY_FLAG_READ;
530 void Entry::markStar(bool mark_star) {
531 /* Check if the starred flag differs from the requested state */
532 if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star)
535 QNetworkRequest request;
536 request.setUrl(feed->reader->edittag_url);
538 poststar.open(QBuffer::ReadWrite | QBuffer::Truncate);
539 poststar.write("i=");
540 poststar.write(QUrl::toPercentEncoding(id));
542 poststar.write("&a=");
544 poststar.write("&r=");
545 poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred"));
546 poststar.write("&ac=edit-tags&T=");
547 poststar.write(QUrl::toPercentEncoding(feed->reader->token));
549 feed->reader->manager.post(request, &poststar);
551 Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred");
554 starred->addEntry(this);
555 flags |= ENTRY_FLAG_STARRED;
558 starred->delEntry(this);
559 flags &= ~ENTRY_FLAG_STARRED;
563 void Entry::markShared(bool mark_shared) {
564 /* Check if the shared flag differs from the requested state */
565 if(((flags & ENTRY_FLAG_SHARED) != 0) == mark_shared)
568 QNetworkRequest request;
569 request.setUrl(feed->reader->edittag_url);
571 postshared.open(QBuffer::ReadWrite | QBuffer::Truncate);
572 postshared.write("i=");
573 postshared.write(QUrl::toPercentEncoding(id));
575 postshared.write("&a=");
577 postshared.write("&r=");
578 postshared.write(QUrl::toPercentEncoding("user/-/state/com.google/broadcast"));
579 postshared.write("&ac=edit-tags&T=");
580 postshared.write(QUrl::toPercentEncoding(feed->reader->token));
582 feed->reader->manager.post(request, &postshared);
584 Feed *shared = feed->reader->feeds.value("user/-/state/com.google/broadcast");
587 shared->addEntry(this);
588 flags |= ENTRY_FLAG_SHARED;
591 shared->delEntry(this);
592 flags &= ~ENTRY_FLAG_SHARED;