2 #include <QApplication>
3 #include <QNetworkRequest>
4 #include <QNetworkReply>
5 #include <QNetworkCookie>
11 #include <qjson/parser.h>
17 #include "googlereader.h"
19 void Feed::updateSubscription(Feed *feed) {
21 sortid = feed->sortid;
22 firstitemmsec = feed->firstitemmsec;
23 cat_id = feed->cat_id;
24 cat_label = feed->cat_label;
25 subscription_updated = true;
28 void Feed::fetch(bool cont) {
29 QNetworkRequest request;
30 //QByteArray ba = "http://www.google.com/reader/atom/";
31 QByteArray ba = "http://www.google.com/reader/api/0/stream/contents/";
32 ba.append(QUrl::toPercentEncoding(id));
33 QUrl url = QUrl::fromEncoded(ba);
35 if(continuation != "" && cont)
36 url.addEncodedQueryItem("c", continuation.toUtf8());
39 reader->getManager()->get(request);
42 GoogleReader::GoogleReader() {
43 connect(&manager, SIGNAL(finished(QNetworkReply*)),
44 SLOT(downloadFinished(QNetworkReply*)));
47 updateSubscriptionsPending = false;
48 updateUnreadPending = false;
51 login_url.setUrl("https://www.google.com/accounts/ClientLogin");
52 subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list?output=json");
53 unread_url.setUrl("http://www.google.com/reader/api/0/unread-count?output=json");
54 edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-");
55 token_url.setUrl("http://www.google.com/reader/api/0/token");
56 markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-");
58 /* Add the virtual 'Starred items' feed */
59 Feed *feed = new Feed(this);
60 feed->id = "user/-/state/com.google/starred";
61 feed->title = "Starred items";
63 feeds.insert(feed->id, feed);
64 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
66 /* Add the virtual 'Shared items' feed */
67 feed = new Feed(this);
68 feed->id = "user/-/state/com.google/broadcast";
69 feed->title = "Shared items";
71 feeds.insert(feed->id, feed);
72 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
75 void GoogleReader::downloadFinished(QNetworkReply *reply) {
76 QUrl url = reply->url();
78 /* TODO: Instead of comparing against the url, use the signal from the
82 qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString());
83 if(url == login_url) {
85 emit loginFailed("Incorrect username or password");
87 else if(url == edittag_url)
91 else if(url == login_url) {
92 QByteArray data = reply->readAll();
93 data.remove(0, data.indexOf("SID=", 0) + 4);
94 data.remove(data.indexOf("\n", 0), 1024);
95 SID = strdup(data.data());
97 qDebug() << "SID:" << SID;
99 manager.cookieJar()->setCookiesFromUrl(
100 QList<QNetworkCookie>() << QNetworkCookie("SID", SID),
101 QUrl("http://www.google.com"));
107 /* TODO: Replace this with a proper state machine */
108 if(updateSubscriptionsPending) {
109 updateSubscriptionsPending = false;
110 updateSubscriptions();
113 else if(url == token_url) {
114 token = reply->readAll();
115 qDebug() << "token:" << token;
117 else if(url == subscriptions_url) {
118 parseSubscriptions(reply->readAll());
120 /* TODO: Replace this with a proper state machine */
121 if(updateUnreadPending) {
122 updateUnreadPending = false;
126 else if(url == unread_url) {
127 parseUnread(reply->readAll());
129 else if(url == edittag_url) {
130 QByteArray data = reply->readAll();
131 //qDebug() << "Result:" << data;
133 else if(url == markallread_url) {
134 QByteArray data = reply->readAll();
135 //qDebug() << "Result:" << data;
138 parseFeed(reply->readAll());
141 reply->deleteLater();
144 void GoogleReader::parseFeed(QByteArray data) {
145 QJson::Parser parser;
147 QVariantMap result = parser.parse(data, &ok).toMap();
148 QString continuation, feedsource, id;
151 id = result["id"].toString();
152 continuation = result["continuation"].toString();
154 /* TODO: This hack should not be needed.. Figure out a cleaner way */
155 if(id.endsWith("/state/com.google/starred"))
156 feedsource = "user/-/state/com.google/starred";
157 else if(id.endsWith("/state/com.google/broadcast"))
158 feedsource = "user/-/state/com.google/broadcast";
160 foreach(QVariant l, result["items"].toList()) {
161 QVariantMap e = l.toMap();
163 QString content, summary;
165 entry.id = e["id"].toString();
166 entry.published = QDateTime::fromTime_t(e["published"].toUInt());
167 entry.author = e["author"].toString();
168 entry.source = (e["origin"].toMap())["streamId"].toString();
169 entry.link = (e["alternate"].toMap())["href"].toString();
171 content = (e["content"].toMap())["content"].toString();
172 summary = (e["summary"].toMap())["content"].toString();
173 if(content != "") entry.content = content; else entry.content = summary;
175 if(e["isReadStateLocked"].toBool())
176 entry.flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
179 p.mainFrame()->setHtml(e["title"].toString());
180 entry.title = p.mainFrame()->toPlainText();
182 foreach(QVariant c, e["categories"].toList()) {
183 QString cat = c.toString();
184 if(cat.endsWith("/state/com.google/read"))
185 entry.flags |= ENTRY_FLAG_READ;
186 else if(cat.endsWith("/state/com.google/starred"))
187 entry.flags |= ENTRY_FLAG_STARRED;
188 else if(cat.endsWith("/state/com.google/broadcast"))
189 entry.flags |= ENTRY_FLAG_SHARED;
193 feed = feeds.value(feedsource == "" ? entry.source : feedsource);
197 feed->addEntry(new Entry(entry));
202 feed->lastUpdated = QDateTime::currentDateTime();
203 feed->continuation = continuation;
204 feed->signalUpdated();
208 void GoogleReader::parseSubscriptions(QByteArray data) {
209 QJson::Parser parser;
211 QVariantMap result = parser.parse(data, &ok).toMap();
213 /* Clear the subscription updated flag */
214 QHash<QString, Feed *>::iterator i;
215 for(i = feeds.begin(); i != feeds.end(); ++i)
216 i.value()->subscription_updated = false;
218 foreach(QVariant l, result["subscriptions"].toList()) {
219 QVariantMap subscription = l.toMap();
220 Feed *feed = new Feed(this);
223 feed->id = subscription["id"].toString();
224 feed->title = subscription["title"].toString();
225 feed->sortid = subscription["sortid"].toString();
226 feed->firstitemmsec = subscription["firstitemmsec"].toString();
228 foreach(QVariant c, subscription["categories"].toList()) {
229 QVariantMap cat = c.toMap();
230 feed->cat_id = cat["id"].toString();
231 feed->cat_label = cat["label"].toString();
234 existing_feed = feeds.value(feed->id);
236 existing_feed->updateSubscription(feed);
238 feed = existing_feed;
242 feed->subscription_updated = true;
243 feeds.insert(feed->id, feed);
244 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
248 /* Delete feeds no longer subscribed to */
249 for(i = feeds.begin(); i != feeds.end(); ++i) {
250 if(i.value()->subscription_updated == false && i.value()->special == 0) {
251 printf("DELETED: %s\n", i.value()->title.toLatin1().data());
256 lastUpdated = QDateTime::currentDateTime();
257 emit updateSubscriptionsComplete();
260 void GoogleReader::parseUnread(QByteArray data) {
261 QJson::Parser parser;
263 QVariantMap result = parser.parse(data, &ok).toMap();
265 foreach(QVariant l, result["unreadcounts"].toList()) {
266 QVariantMap unread = l.toMap();
267 QString id = unread["id"].toString();
268 int count = unread["count"].toInt();
269 ulong newestitem = unread["newestitem"].toUInt();
271 Feed *f = feeds.value(id);
274 f->newestitem = newestitem;
278 lastUpdated = QDateTime::currentDateTime();
279 emit updateUnreadComplete();
282 void GoogleReader::getSID() {
289 QNetworkRequest request;
290 request.setUrl(login_url);
292 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
293 buffer.write("Email=");
294 buffer.write(QUrl::toPercentEncoding(login));
295 buffer.write("&Passwd=");
296 buffer.write(QUrl::toPercentEncoding(passwd));
297 buffer.write("&service=reader&source=grms&continue=http://www.google.com");
300 //qDebug() << buffer.readAll();
303 manager.post(request, &buffer);
306 void GoogleReader::getToken() {
307 QNetworkRequest request;
308 request.setUrl(token_url);
309 manager.get(request);
312 void GoogleReader::updateSubscriptions() {
313 QNetworkRequest request;
315 if(updateSubscriptionsPending)
319 updateSubscriptionsPending = true;
324 request.setUrl(subscriptions_url);
325 manager.get(request);
328 void GoogleReader::updateUnread() {
329 QNetworkRequest request;
331 if(updateUnreadPending)
335 updateUnreadPending = true;
340 request.setUrl(unread_url);
341 manager.get(request);
344 static bool compareFeedItems(const Feed *f1, const Feed *f2) {
345 if(f1->special || f2->special)
346 return f1->special > f2->special;
348 if(f1->cat_label == f2->cat_label)
349 return f1->title.toLower() < f2->title.toLower();
351 if(f1->cat_label.length() == 0)
354 if(f2->cat_label.length() == 0)
357 return f1->cat_label.toLower() < f2->cat_label.toLower();
360 QList<Feed *> GoogleReader::getFeeds() {
361 QList<Feed *> list = feeds.values();
362 qSort(list.begin(), list.end(), compareFeedItems);
366 void Feed::addEntry(Entry *entry) {
367 entries.insert(entry->id, entry);
370 void Feed::delEntry(Entry *entry) {
371 entries.remove(entry->id);
374 void Feed::signalUpdated() {
375 // TODO: Clean this up
376 emit updateFeedComplete();
379 void Feed::updateUnread(int i) {
380 bool allRead = (unread == 0);
383 if(unread <= 0) unread = 0;
385 if(allRead != (unread == 0))
386 emit allReadChanged();
389 void Feed::markRead() {
391 /* Mark all the remaining items read */
393 QNetworkRequest request;
394 request.setUrl(reader->markallread_url);
396 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
398 buffer.write(QUrl::toPercentEncoding(id));
399 //buffer.write("&ts=");
400 //buffer.write(QByteArray::number(oldest));
402 buffer.write(QUrl::toPercentEncoding(reader->token));
405 //qDebug() << buffer.readAll();
408 reader->manager.post(request, &buffer);
412 /* Go over all the entries and mark them read */
413 QHash<QString, Entry *>::iterator i;
414 for(i = entries.begin(); i != entries.end(); ++i)
415 i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED;
418 emit allReadChanged();
421 static bool compareEntryItems(const Entry *e1, const Entry *e2) {
422 return e1->published > e2->published;
425 QList<Entry *> Feed::getEntries() {
426 QList<Entry *> list = entries.values();
427 qSort(list.begin(), list.end(), compareEntryItems);
431 /* TODO: Remove the duplicate code in changing stated */
433 void Entry::markRead(bool mark_read) {
434 /* Check if the read flag differs from the requested state */
435 if(((flags & ENTRY_FLAG_READ) != 0) == mark_read)
438 /* Cannot mark an item unread if it's locked */
439 if((flags & ENTRY_FLAG_LOCKED) && !mark_read)
442 QNetworkRequest request;
443 request.setUrl(feed->reader->edittag_url);
445 postread.open(QBuffer::ReadWrite | QBuffer::Truncate);
446 postread.write("i=");
447 postread.write(QUrl::toPercentEncoding(id));
449 postread.write("&a=");
451 postread.write("&r=");
452 postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read"));
453 postread.write("&ac=edit-tags&T=");
454 postread.write(QUrl::toPercentEncoding(feed->reader->token));
456 feed->reader->manager.post(request, &postread);
458 feed->updateUnread(mark_read ? -1 : 1);
461 flags |= ENTRY_FLAG_READ;
463 flags &= ~ENTRY_FLAG_READ;
466 void Entry::markStar(bool mark_star) {
467 /* Check if the starred flag differs from the requested state */
468 if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star)
471 QNetworkRequest request;
472 request.setUrl(feed->reader->edittag_url);
474 poststar.open(QBuffer::ReadWrite | QBuffer::Truncate);
475 poststar.write("i=");
476 poststar.write(QUrl::toPercentEncoding(id));
478 poststar.write("&a=");
480 poststar.write("&r=");
481 poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred"));
482 poststar.write("&ac=edit-tags&T=");
483 poststar.write(QUrl::toPercentEncoding(feed->reader->token));
485 feed->reader->manager.post(request, &poststar);
487 Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred");
490 starred->addEntry(this);
491 flags |= ENTRY_FLAG_STARRED;
494 starred->delEntry(this);
495 flags &= ~ENTRY_FLAG_STARRED;
499 void Entry::markShared(bool mark_shared) {
500 /* Check if the shared flag differs from the requested state */
501 if(((flags & ENTRY_FLAG_SHARED) != 0) == mark_shared)
504 QNetworkRequest request;
505 request.setUrl(feed->reader->edittag_url);
507 postshared.open(QBuffer::ReadWrite | QBuffer::Truncate);
508 postshared.write("i=");
509 postshared.write(QUrl::toPercentEncoding(id));
511 postshared.write("&a=");
513 postshared.write("&r=");
514 postshared.write(QUrl::toPercentEncoding("user/-/state/com.google/broadcast"));
515 postshared.write("&ac=edit-tags&T=");
516 postshared.write(QUrl::toPercentEncoding(feed->reader->token));
518 feed->reader->manager.post(request, &postshared);
520 Feed *shared = feed->reader->feeds.value("user/-/state/com.google/broadcast");
523 shared->addEntry(this);
524 flags |= ENTRY_FLAG_SHARED;
527 shared->delEntry(this);
528 flags &= ~ENTRY_FLAG_SHARED;