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 reply = reader->getManager()->get(request);
40 connect(reply, SIGNAL(finished()), SLOT(fetchFinished()));
43 void Feed::fetchFinished() {
45 qDebug() << "Download of" << reply->url() << "failed:" << qPrintable(reply->errorString());
51 QVariantMap result = parser.parse(reply->readAll(), &ok).toMap();
54 continuation = result["continuation"].toString();
56 foreach(QVariant l, result["items"].toList()) {
57 QVariantMap e = l.toMap();
58 Entry *entry = new Entry();;
59 QString content, summary;
61 entry->id = e["id"].toString();
62 entry->published = QDateTime::fromTime_t(e["published"].toUInt());
63 entry->author = e["author"].toString();
64 entry->source = (e["origin"].toMap())["streamId"].toString();
65 entry->link = (e["alternate"].toMap())["href"].toString();
67 content = (e["content"].toMap())["content"].toString();
68 summary = (e["summary"].toMap())["content"].toString();
69 if(content != "") entry->content = content; else entry->content = summary;
71 if(e["isReadStateLocked"].toBool())
72 entry->flags |= ENTRY_FLAG_LOCKED | ENTRY_FLAG_READ;
75 p.mainFrame()->setHtml(e["title"].toString());
76 entry->title = p.mainFrame()->toPlainText();
78 foreach(QVariant c, e["categories"].toList()) {
79 QString cat = c.toString();
80 if(cat.endsWith("/state/com.google/read"))
81 entry->flags |= ENTRY_FLAG_READ;
82 else if(cat.endsWith("/state/com.google/starred"))
83 entry->flags |= ENTRY_FLAG_STARRED;
84 else if(cat.endsWith("/state/com.google/broadcast"))
85 entry->flags |= ENTRY_FLAG_SHARED;
92 lastUpdated = QDateTime::currentDateTime();
94 emit updateFeedComplete();
99 GoogleReader::GoogleReader() {
100 connect(&manager, SIGNAL(finished(QNetworkReply*)),
101 SLOT(downloadFinished(QNetworkReply*)));
104 updateSubscriptionsPending = false;
105 updateUnreadPending = false;
108 login_url.setUrl("https://www.google.com/accounts/ClientLogin");
109 subscriptions_url.setUrl("http://www.google.com/reader/api/0/subscription/list?output=json");
110 unread_url.setUrl("http://www.google.com/reader/api/0/unread-count?output=json");
111 edittag_url.setUrl("http://www.google.com/reader/api/0/edit-tag?client=-");
112 token_url.setUrl("http://www.google.com/reader/api/0/token");
113 markallread_url.setUrl("http://www.google.com/reader/api/0/mark-all-as-read?client=-");
115 /* Add the virtual 'Starred items' feed */
116 Feed *feed = new Feed(this);
117 feed->id = "user/-/state/com.google/starred";
118 feed->title = "Starred items";
120 feeds.insert(feed->id, feed);
121 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
123 /* Add the virtual 'Shared items' feed */
124 feed = new Feed(this);
125 feed->id = "user/-/state/com.google/broadcast";
126 feed->title = "Shared items";
128 feeds.insert(feed->id, feed);
129 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
132 void GoogleReader::downloadFinished(QNetworkReply *reply) {
133 QUrl url = reply->url();
135 /* TODO: Instead of comparing against the url, use the signal from the
136 * QNetworkReply... */
138 if (reply->error()) {
139 qDebug() << "Download of" << url << "failed:" << qPrintable(reply->errorString());
140 if(url == login_url) {
142 emit loginFailed("Incorrect username or password");
144 else if(url == edittag_url)
148 else if(url == login_url) {
149 QByteArray data = reply->readAll();
150 data.remove(0, data.indexOf("SID=", 0) + 4);
151 data.remove(data.indexOf("\n", 0), 1024);
152 SID = strdup(data.data());
154 qDebug() << "SID:" << SID;
156 manager.cookieJar()->setCookiesFromUrl(
157 QList<QNetworkCookie>() << QNetworkCookie("SID", SID),
158 QUrl("http://www.google.com"));
164 /* TODO: Replace this with a proper state machine */
165 if(updateSubscriptionsPending) {
166 updateSubscriptionsPending = false;
167 updateSubscriptions();
170 else if(url == token_url) {
171 token = reply->readAll();
172 qDebug() << "token:" << token;
174 else if(url == subscriptions_url) {
175 parseSubscriptions(reply->readAll());
177 /* TODO: Replace this with a proper state machine */
178 if(updateUnreadPending) {
179 updateUnreadPending = false;
183 else if(url == unread_url) {
184 parseUnread(reply->readAll());
186 else if(url == edittag_url) {
187 QByteArray data = reply->readAll();
188 //qDebug() << "Result:" << data;
190 else if(url == markallread_url) {
191 QByteArray data = reply->readAll();
192 //qDebug() << "Result:" << data;
195 reply->deleteLater();
198 void GoogleReader::parseSubscriptions(QByteArray data) {
199 QJson::Parser parser;
201 QVariantMap result = parser.parse(data, &ok).toMap();
203 /* Clear the subscription updated flag */
204 QHash<QString, Feed *>::iterator i;
205 for(i = feeds.begin(); i != feeds.end(); ++i)
206 i.value()->subscription_updated = false;
208 foreach(QVariant l, result["subscriptions"].toList()) {
209 QVariantMap subscription = l.toMap();
210 Feed *feed = new Feed(this);
213 feed->id = subscription["id"].toString();
214 feed->title = subscription["title"].toString();
215 feed->sortid = subscription["sortid"].toString();
216 feed->firstitemmsec = subscription["firstitemmsec"].toString();
218 foreach(QVariant c, subscription["categories"].toList()) {
219 QVariantMap cat = c.toMap();
220 feed->cat_id = cat["id"].toString();
221 feed->cat_label = cat["label"].toString();
224 existing_feed = feeds.value(feed->id);
226 existing_feed->updateSubscription(feed);
228 feed = existing_feed;
232 feed->subscription_updated = true;
233 feeds.insert(feed->id, feed);
234 connect(feed, SIGNAL(allReadChanged()), SIGNAL(allReadChanged()));
238 /* Delete feeds no longer subscribed to */
239 for(i = feeds.begin(); i != feeds.end(); ++i) {
240 if(i.value()->subscription_updated == false && i.value()->special == 0) {
241 printf("DELETED: %s\n", i.value()->title.toLatin1().data());
246 lastUpdated = QDateTime::currentDateTime();
247 emit updateSubscriptionsComplete();
250 void GoogleReader::parseUnread(QByteArray data) {
251 QJson::Parser parser;
253 QVariantMap result = parser.parse(data, &ok).toMap();
255 foreach(QVariant l, result["unreadcounts"].toList()) {
256 QVariantMap unread = l.toMap();
257 QString id = unread["id"].toString();
258 int count = unread["count"].toInt();
259 ulong newestitem = unread["newestitem"].toUInt();
261 Feed *f = feeds.value(id);
264 f->newestitem = newestitem;
268 lastUpdated = QDateTime::currentDateTime();
269 emit updateUnreadComplete();
272 void GoogleReader::getSID() {
279 QNetworkRequest request;
280 request.setUrl(login_url);
282 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
283 buffer.write("Email=");
284 buffer.write(QUrl::toPercentEncoding(login));
285 buffer.write("&Passwd=");
286 buffer.write(QUrl::toPercentEncoding(passwd));
287 buffer.write("&service=reader&source=grms&continue=http://www.google.com");
290 //qDebug() << buffer.readAll();
293 manager.post(request, &buffer);
296 void GoogleReader::getToken() {
297 QNetworkRequest request;
298 request.setUrl(token_url);
299 manager.get(request);
302 void GoogleReader::updateSubscriptions() {
303 QNetworkRequest request;
305 if(updateSubscriptionsPending)
309 updateSubscriptionsPending = true;
314 request.setUrl(subscriptions_url);
315 manager.get(request);
318 void GoogleReader::updateUnread() {
319 QNetworkRequest request;
321 if(updateUnreadPending)
325 updateUnreadPending = true;
330 request.setUrl(unread_url);
331 manager.get(request);
334 static bool compareFeedItems(const Feed *f1, const Feed *f2) {
335 if(f1->special || f2->special)
336 return f1->special > f2->special;
338 if(f1->cat_label == f2->cat_label)
339 return f1->title.toLower() < f2->title.toLower();
341 if(f1->cat_label.length() == 0)
344 if(f2->cat_label.length() == 0)
347 return f1->cat_label.toLower() < f2->cat_label.toLower();
350 QList<Feed *> GoogleReader::getFeeds() {
351 QList<Feed *> list = feeds.values();
352 qSort(list.begin(), list.end(), compareFeedItems);
356 void Feed::addEntry(Entry *entry) {
357 entries.insert(entry->id, entry);
360 void Feed::delEntry(Entry *entry) {
361 entries.remove(entry->id);
364 void Feed::updateUnread(int i) {
365 bool allRead = (unread == 0);
368 if(unread <= 0) unread = 0;
370 if(allRead != (unread == 0))
371 emit allReadChanged();
374 void Feed::markRead() {
376 /* Mark all the remaining items read */
378 QNetworkRequest request;
379 request.setUrl(reader->markallread_url);
381 buffer.open(QBuffer::ReadWrite | QBuffer::Truncate);
383 buffer.write(QUrl::toPercentEncoding(id));
384 //buffer.write("&ts=");
385 //buffer.write(QByteArray::number(oldest));
387 buffer.write(QUrl::toPercentEncoding(reader->token));
390 //qDebug() << buffer.readAll();
393 reader->manager.post(request, &buffer);
397 /* Go over all the entries and mark them read */
398 QHash<QString, Entry *>::iterator i;
399 for(i = entries.begin(); i != entries.end(); ++i)
400 i.value()->flags |= ENTRY_FLAG_READ | ENTRY_FLAG_LOCKED;
403 emit allReadChanged();
406 static bool compareEntryItems(const Entry *e1, const Entry *e2) {
407 return e1->published > e2->published;
410 QList<Entry *> Feed::getEntries() {
411 QList<Entry *> list = entries.values();
412 qSort(list.begin(), list.end(), compareEntryItems);
416 /* TODO: Remove the duplicate code in changing stated */
418 void Entry::markRead(bool mark_read) {
419 /* Check if the read flag differs from the requested state */
420 if(((flags & ENTRY_FLAG_READ) != 0) == mark_read)
423 /* Cannot mark an item unread if it's locked */
424 if((flags & ENTRY_FLAG_LOCKED) && !mark_read)
427 QNetworkRequest request;
428 request.setUrl(feed->reader->edittag_url);
430 postread.open(QBuffer::ReadWrite | QBuffer::Truncate);
431 postread.write("i=");
432 postread.write(QUrl::toPercentEncoding(id));
434 postread.write("&a=");
436 postread.write("&r=");
437 postread.write(QUrl::toPercentEncoding("user/-/state/com.google/read"));
438 postread.write("&ac=edit-tags&T=");
439 postread.write(QUrl::toPercentEncoding(feed->reader->token));
441 feed->reader->manager.post(request, &postread);
443 feed->updateUnread(mark_read ? -1 : 1);
446 flags |= ENTRY_FLAG_READ;
448 flags &= ~ENTRY_FLAG_READ;
451 void Entry::markStar(bool mark_star) {
452 /* Check if the starred flag differs from the requested state */
453 if(((flags & ENTRY_FLAG_STARRED) != 0) == mark_star)
456 QNetworkRequest request;
457 request.setUrl(feed->reader->edittag_url);
459 poststar.open(QBuffer::ReadWrite | QBuffer::Truncate);
460 poststar.write("i=");
461 poststar.write(QUrl::toPercentEncoding(id));
463 poststar.write("&a=");
465 poststar.write("&r=");
466 poststar.write(QUrl::toPercentEncoding("user/-/state/com.google/starred"));
467 poststar.write("&ac=edit-tags&T=");
468 poststar.write(QUrl::toPercentEncoding(feed->reader->token));
470 feed->reader->manager.post(request, &poststar);
472 Feed *starred = feed->reader->feeds.value("user/-/state/com.google/starred");
475 starred->addEntry(this);
476 flags |= ENTRY_FLAG_STARRED;
479 starred->delEntry(this);
480 flags &= ~ENTRY_FLAG_STARRED;
484 void Entry::markShared(bool mark_shared) {
485 /* Check if the shared flag differs from the requested state */
486 if(((flags & ENTRY_FLAG_SHARED) != 0) == mark_shared)
489 QNetworkRequest request;
490 request.setUrl(feed->reader->edittag_url);
492 postshared.open(QBuffer::ReadWrite | QBuffer::Truncate);
493 postshared.write("i=");
494 postshared.write(QUrl::toPercentEncoding(id));
496 postshared.write("&a=");
498 postshared.write("&r=");
499 postshared.write(QUrl::toPercentEncoding("user/-/state/com.google/broadcast"));
500 postshared.write("&ac=edit-tags&T=");
501 postshared.write(QUrl::toPercentEncoding(feed->reader->token));
503 feed->reader->manager.post(request, &postshared);
505 Feed *shared = feed->reader->feeds.value("user/-/state/com.google/broadcast");
508 shared->addEntry(this);
509 flags |= ENTRY_FLAG_SHARED;
512 shared->delEntry(this);
513 flags &= ~ENTRY_FLAG_SHARED;