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/api/0/stream/contents/";
31 ba.append(QUrl::toPercentEncoding(id));
32 QUrl url = QUrl::fromEncoded(ba);
34 if(continuation != "" && cont)
35 url.addEncodedQueryItem("c", continuation.toUtf8());
37 if(!cont && updated) {
38 /* Add 1 to the timestamp, otherwise we get the latest item
39 * again. Also the order has to be reversed for this to work. */
40 url.addEncodedQueryItem("ot", QByteArray::number(updated + 1));
41 url.addEncodedQueryItem("r", "o");
45 reply = reader->getManager()->get(request);
46 connect(reply, SIGNAL(finished()), SLOT(fetchFinished()));
49 void Feed::fetchFinished() {
51 qDebug() << "Download of" << reply->url() << "failed:" << qPrintable(reply->errorString());
57 QVariantMap result = parser.parse(reply->readAll(), &ok).toMap();
60 continuation = result["continuation"].toString();
61 updated = result["updated"].toUInt();
63 foreach(QVariant l, result["items"].toList()) {
64 QVariantMap e = l.toMap();
65 Entry *entry = new Entry();;
66 QString content, summary;
68 entry->id = e["id"].toString();
69 entry->published = QDateTime::fromTime_t(e["published"].toUInt());
70 entry->author = e["author"].toString();
71 entry->source = (e["origin"].toMap())["streamId"].toString();
72 entry->link = (e["alternate"].toMap())["href"].toString();
74 content = (e["content"].toMap())["content"].toString();
75 summary = (e["summary"].toMap())["content"].toString();
76 if(content != "") entry->content = content; else entry->content = summary;
78 if(e["isReadStateLocked"].toBool())
79 entry->flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
82 p.mainFrame()->setHtml(e["title"].toString());
83 entry->title = p.mainFrame()->toPlainText();
85 foreach(QVariant c, e["categories"].toList()) {
86 QString cat = c.toString();
87 if(cat.endsWith("/state/com.google/read"))
88 entry->flags |= ENTRY_FLAG_READ;
89 else if(cat.endsWith("/state/com.google/starred"))
90 entry->flags |= ENTRY_FLAG_STARRED;
91 else if(cat.endsWith("/state/com.google/broadcast"))
92 entry->flags |= ENTRY_FLAG_SHARED;
99 lastUpdated = QDateTime::currentDateTime();
101 emit updateFeedComplete();
103 reply->deleteLater();
106 GoogleReader::GoogleReader() {
107 connect(&manager, SIGNAL(finished(QNetworkReply*)),
108 SLOT(downloadFinished(QNetworkReply*)));
111 updateSubscriptionsPending = false;
112 updateUnreadPending = false;
115 login_url.setUrl("https://www.google.com/accounts/ClientLogin");
116 subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list?output=json");
117 unread_url.setUrl("http://www.google.com/reader/api/0/unread-count?output=json");
118 edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-");
119 token_url.setUrl("http://www.google.com/reader/api/0/token");
120 markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-");
122 /* Add the virtual 'Starred items' feed */
123 Feed *feed = new Feed(this);
124 feed->id = "user/-/state/com.google/starred";
125 feed->title = "Starred items";
127 feeds.insert(feed->id, feed);
128 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
130 /* Add the virtual 'Shared items' feed */
131 feed = new Feed(this);
132 feed->id = "user/-/state/com.google/broadcast";
133 feed->title = "Shared items";
135 feeds.insert(feed->id, feed);
136 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
139 void GoogleReader::downloadFinished(QNetworkReply *reply) {
140 QUrl url = reply->url();
142 /* TODO: Instead of comparing against the url, use the signal from the
143 * QNetworkReply... */
145 if (reply->error()) {
146 qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString());
147 if(url == login_url) {
149 emit loginFailed("Incorrect username or password");
151 else if(url == edittag_url)
155 else if(url == login_url) {
156 QByteArray data = reply->readAll();
157 data.remove(0, data.indexOf("SID=", 0) + 4);
158 data.remove(data.indexOf("\n", 0), 1024);
159 SID = strdup(data.data());
161 qDebug() << "SID:" << SID;
163 manager.cookieJar()->setCookiesFromUrl(
164 QList<QNetworkCookie>() << QNetworkCookie("SID", SID),
165 QUrl("http://www.google.com"));
171 /* TODO: Replace this with a proper state machine */
172 if(updateSubscriptionsPending) {
173 updateSubscriptionsPending = false;
174 updateSubscriptions();
177 else if(url == token_url) {
178 token = reply->readAll();
179 qDebug() << "token:" << token;
181 else if(url == subscriptions_url) {
182 parseSubscriptions(reply->readAll());
184 /* TODO: Replace this with a proper state machine */
185 if(updateUnreadPending) {
186 updateUnreadPending = false;
190 else if(url == unread_url) {
191 parseUnread(reply->readAll());
193 else if(url == edittag_url) {
194 QByteArray data = reply->readAll();
195 //qDebug() << "Result:" << data;
197 else if(url == markallread_url) {
198 QByteArray data = reply->readAll();
199 //qDebug() << "Result:" << data;
202 reply->deleteLater();
205 void GoogleReader::parseSubscriptions(QByteArray data) {
206 QJson::Parser parser;
208 QVariantMap result = parser.parse(data, &ok).toMap();
210 /* Clear the subscription updated flag */
211 QHash<QString, Feed *>::iterator i;
212 for(i = feeds.begin(); i != feeds.end(); ++i)
213 i.value()->subscription_updated = false;
215 foreach(QVariant l, result["subscriptions"].toList()) {
216 QVariantMap subscription = l.toMap();
217 Feed *feed = new Feed(this);
220 feed->id = subscription["id"].toString();
221 feed->title = subscription["title"].toString();
222 feed->sortid = subscription["sortid"].toString();
223 feed->firstitemmsec = subscription["firstitemmsec"].toString();
225 foreach(QVariant c, subscription["categories"].toList()) {
226 QVariantMap cat = c.toMap();
227 feed->cat_id = cat["id"].toString();
228 feed->cat_label = cat["label"].toString();
231 existing_feed = feeds.value(feed->id);
233 existing_feed->updateSubscription(feed);
235 feed = existing_feed;
239 feed->subscription_updated = true;
240 feeds.insert(feed->id, feed);
241 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
245 /* Delete feeds no longer subscribed to */
246 for(i = feeds.begin(); i != feeds.end(); ++i) {
247 if(i.value()->subscription_updated == false && i.value()->special == 0) {
248 printf("DELETED: %s\n", i.value()->title.toLatin1().data());
253 lastUpdated = QDateTime::currentDateTime();
254 emit updateSubscriptionsComplete();
257 void GoogleReader::parseUnread(QByteArray data) {
258 QJson::Parser parser;
260 QVariantMap result = parser.parse(data, &ok).toMap();
262 foreach(QVariant l, result["unreadcounts"].toList()) {
263 QVariantMap unread = l.toMap();
264 QString id = unread["id"].toString();
265 int count = unread["count"].toInt();
266 ulong newestitem = unread["newestitem"].toUInt();
268 Feed *f = feeds.value(id);
271 f->newestitem = newestitem;
273 /* Not a good idea if it doesn't happen sequentially. */
274 /* Pre-fetch feeds with unread items */
275 /* f->fetch(false); */
279 lastUpdated = QDateTime::currentDateTime();
280 emit updateUnreadComplete();
283 void GoogleReader::getSID() {
290 QNetworkRequest request;
291 request.setUrl(login_url);
293 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
294 buffer.write("Email=");
295 buffer.write(QUrl::toPercentEncoding(login));
296 buffer.write("&Passwd=");
297 buffer.write(QUrl::toPercentEncoding(passwd));
298 buffer.write("&service=reader&source=grms&continue=http://www.google.com");
301 //qDebug() << buffer.readAll();
304 manager.post(request, &buffer);
307 void GoogleReader::getToken() {
308 QNetworkRequest request;
309 request.setUrl(token_url);
310 manager.get(request);
313 void GoogleReader::updateSubscriptions() {
314 QNetworkRequest request;
316 if(updateSubscriptionsPending)
320 updateSubscriptionsPending = true;
325 request.setUrl(subscriptions_url);
326 manager.get(request);
329 void GoogleReader::updateUnread() {
330 QNetworkRequest request;
332 if(updateUnreadPending)
336 updateUnreadPending = true;
341 request.setUrl(unread_url);
342 manager.get(request);
345 static bool compareFeedItems(const Feed *f1, const Feed *f2) {
346 if(f1->special || f2->special)
347 return f1->special > f2->special;
349 if(f1->cat_label == f2->cat_label)
350 return f1->title.toLower() < f2->title.toLower();
352 if(f1->cat_label.length() == 0)
355 if(f2->cat_label.length() == 0)
358 return f1->cat_label.toLower() < f2->cat_label.toLower();
361 QList<Feed *> GoogleReader::getFeeds() {
362 QList<Feed *> list = feeds.values();
363 qSort(list.begin(), list.end(), compareFeedItems);
367 void Feed::addEntry(Entry *entry) {
368 entries.insert(entry->id, entry);
371 void Feed::delEntry(Entry *entry) {
372 entries.remove(entry->id);
375 void Feed::updateUnread(int i) {
376 bool allRead = (unread == 0);
379 if(unread <= 0) unread = 0;
381 if(allRead != (unread == 0))
382 emit allReadChanged();
385 void Feed::markRead() {
387 /* Mark all the remaining items read */
389 QNetworkRequest request;
390 request.setUrl(reader->markallread_url);
392 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
394 buffer.write(QUrl::toPercentEncoding(id));
395 //buffer.write("&ts=");
396 //buffer.write(QByteArray::number(oldest));
398 buffer.write(QUrl::toPercentEncoding(reader->token));
401 //qDebug() << buffer.readAll();
404 reader->manager.post(request, &buffer);
408 /* Go over all the entries and mark them read */
409 QHash<QString, Entry *>::iterator i;
410 for(i = entries.begin(); i != entries.end(); ++i)
411 i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED;
414 emit allReadChanged();
417 static bool compareEntryItems(const Entry *e1, const Entry *e2) {
418 return e1->published > e2->published;
421 QList<Entry *> Feed::getEntries() {
422 QList<Entry *> list = entries.values();
423 qSort(list.begin(), list.end(), compareEntryItems);
427 /* TODO: Remove the duplicate code in changing stated */
429 void Entry::markRead(bool mark_read) {
430 /* Check if the read flag differs from the requested state */
431 if(((flags & ENTRY_FLAG_READ) != 0) == mark_read)
434 /* Cannot mark an item unread if it's locked */
435 if((flags & ENTRY_FLAG_LOCKED) && !mark_read)
438 QNetworkRequest request;
439 request.setUrl(feed->reader->edittag_url);
441 postread.open(QBuffer::ReadWrite | QBuffer::Truncate);
442 postread.write("i=");
443 postread.write(QUrl::toPercentEncoding(id));
445 postread.write("&a=");
447 postread.write("&r=");
448 postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read"));
449 postread.write("&ac=edit-tags&T=");
450 postread.write(QUrl::toPercentEncoding(feed->reader->token));
452 feed->reader->manager.post(request, &postread);
454 feed->updateUnread(mark_read ? -1 : 1);
457 flags |= ENTRY_FLAG_READ;
459 flags &= ~ENTRY_FLAG_READ;
462 void Entry::markStar(bool mark_star) {
463 /* Check if the starred flag differs from the requested state */
464 if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star)
467 QNetworkRequest request;
468 request.setUrl(feed->reader->edittag_url);
470 poststar.open(QBuffer::ReadWrite | QBuffer::Truncate);
471 poststar.write("i=");
472 poststar.write(QUrl::toPercentEncoding(id));
474 poststar.write("&a=");
476 poststar.write("&r=");
477 poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred"));
478 poststar.write("&ac=edit-tags&T=");
479 poststar.write(QUrl::toPercentEncoding(feed->reader->token));
481 feed->reader->manager.post(request, &poststar);
483 Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred");
486 starred->addEntry(this);
487 flags |= ENTRY_FLAG_STARRED;
490 starred->delEntry(this);
491 flags &= ~ENTRY_FLAG_STARRED;
495 void Entry::markShared(bool mark_shared) {
496 /* Check if the shared flag differs from the requested state */
497 if(((flags & ENTRY_FLAG_SHARED) != 0) == mark_shared)
500 QNetworkRequest request;
501 request.setUrl(feed->reader->edittag_url);
503 postshared.open(QBuffer::ReadWrite | QBuffer::Truncate);
504 postshared.write("i=");
505 postshared.write(QUrl::toPercentEncoding(id));
507 postshared.write("&a=");
509 postshared.write("&r=");
510 postshared.write(QUrl::toPercentEncoding("user/-/state/com.google/broadcast"));
511 postshared.write("&ac=edit-tags&T=");
512 postshared.write(QUrl::toPercentEncoding(feed->reader->token));
514 feed->reader->manager.post(request, &postshared);
516 Feed *shared = feed->reader->feeds.value("user/-/state/com.google/broadcast");
519 shared->addEntry(this);
520 flags |= ENTRY_FLAG_SHARED;
523 shared->delEntry(this);
524 flags &= ~ENTRY_FLAG_SHARED;