4
#include <google_api.h>
10
#define GREADER_LOGIN "https://www.google.com/accounts/ClientLogin"
11
#define GREADER_API_PREFIX "http://www.google.com/reader/api/0/"
12
#define GREADER_FEED_PREFIX "http://www.google.com/reader/atom/"
14
#define GREADER_SUBSCRIPTION_LIST GREADER_API_PREFIX "subscription/list"
15
#define GREADER_API_MARK_ALL_READ_URL GREADER_API_PREFIX "mark-all-as-read"
16
#define GREADER_API_EDIT_TAG_URL GREADER_API_PREFIX "edit-tag"
17
#define GREADER_API_TOKEN_URL GREADER_API_PREFIX "token"
19
// for reference, see http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
21
namespace newsbeuter {
23
googlereader_api::googlereader_api(configcontainer * c) : remote_api(c) {
27
googlereader_api::~googlereader_api() {
31
bool googlereader_api::authenticate() {
33
LOG(LOG_DEBUG, "googlereader_api::authenticate: SID = %s", sid.c_str());
37
static size_t my_write_data(void *buffer, size_t size, size_t nmemb, void *userp) {
38
std::string * pbuf = static_cast<std::string *>(userp);
39
pbuf->append(static_cast<const char *>(buffer), size * nmemb);
43
std::string googlereader_api::retrieve_sid() {
44
CURL * handle = curl_easy_init();
45
std::string postcontent = utils::strprintf("service=reader&Email=%s&Passwd=%s&source=%s/%s&continue=http://www.google.com/",
46
cfg->get_configvalue("googlereader-login").c_str(), cfg->get_configvalue("googlereader-password").c_str(), PROGRAM_NAME, PROGRAM_VERSION);
49
utils::set_common_curl_options(handle, cfg);
50
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_write_data);
51
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &result);
52
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, postcontent.c_str());
53
curl_easy_setopt(handle, CURLOPT_URL, GREADER_LOGIN);
54
curl_easy_perform(handle);
55
curl_easy_cleanup(handle);
57
std::vector<std::string> lines = utils::tokenize(result);
58
for (std::vector<std::string>::iterator it=lines.begin();it!=lines.end();it++) {
59
LOG(LOG_DEBUG, "googlereader_api::retrieve_sid: line = %s", it->c_str());
60
if (it->substr(0,4)=="SID=") {
61
std::string sid = it->substr(4, it->length()-4);
69
std::vector<tagged_feedurl> googlereader_api::get_subscribed_urls() {
70
std::vector<tagged_feedurl> urls;
72
CURL * handle = curl_easy_init();
74
std::string cookie = utils::strprintf("SID=%s;", sid.c_str());
76
utils::set_common_curl_options(handle, cfg);
77
curl_easy_setopt(handle, CURLOPT_COOKIE, cookie.c_str());
78
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_write_data);
79
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &result);
80
curl_easy_setopt(handle, CURLOPT_URL, GREADER_SUBSCRIPTION_LIST);
81
curl_easy_perform(handle);
82
curl_easy_cleanup(handle);
84
LOG(LOG_DEBUG, "googlereader_api::get_subscribed_urls: document = %s", result.c_str());
86
xmlDoc * doc = xmlParseMemory(result.c_str(), result.length());
89
LOG(LOG_ERROR, "googlreader_api::get_subscribed_urls: failed to parse subscription list");
93
xmlNode * root = xmlDocGetRootElement(doc);
95
LOG(LOG_DEBUG, "googlereader_api::get_subscribed_urls: root = %p", root);
98
for (xmlNode * node = root->children; node != NULL; node = node->next) {
99
if (strcmp((const char *)node->name, "list")==0 && utils::get_prop(node, "name") == "subscriptions") {
100
LOG(LOG_DEBUG, "found subscriptions list");
101
for (xmlNode * objectnode = node->children; objectnode != NULL; objectnode = objectnode->next) {
102
if (strcmp((const char *)objectnode->name, "object")==0) {
103
LOG(LOG_DEBUG, "found object");
106
std::vector<std::string> tags;
107
for (xmlNode * elem = objectnode->children; elem != NULL; elem = elem->next) {
108
if (strcmp((const char *)elem->name, "string")==0 && utils::get_prop(elem,"name")=="id") {
109
LOG(LOG_DEBUG, "found id");
110
id = utils::get_content(elem);
111
} else if (strcmp((const char *)elem->name, "string")==0 && utils::get_prop(elem,"name")=="title") {
112
title = utils::get_content(elem);
113
} else if (strcmp((const char *)elem->name, "list")==0 && utils::get_prop(elem,"name")=="categories") {
114
LOG(LOG_DEBUG, "found tag");
115
tags = get_tags(elem);
120
tags.push_back(utils::strprintf("~%s", title.c_str()));
122
urls.push_back(tagged_feedurl(utils::strprintf("%s%s", GREADER_FEED_PREFIX, utils::escape_url(id).c_str()), tags));
135
std::vector<std::string> googlereader_api::get_tags(xmlNode * node) {
136
std::vector<std::string> tags;
138
if (node && strcmp((const char *)node->name, "list")==0 && utils::get_prop(node, "name")=="categories") {
139
LOG(LOG_DEBUG, "found tag list");
140
for (xmlNode * objnode = node->children; objnode != NULL; objnode = objnode->next) {
141
if (strcmp((const char *)objnode->name, "object")==0) {
142
LOG(LOG_DEBUG, "found tag list object");
143
for (xmlNode * str = objnode->children; str != NULL; str = str->next) {
144
if (strcmp((const char *)str->name, "string")==0 && utils::get_prop(str, "name")=="label") {
145
LOG(LOG_DEBUG, "found tag list label");
146
tags.push_back(utils::get_content(str));
155
void googlereader_api::configure_handle(CURL * handle) {
156
std::string cookie = utils::strprintf("SID=%s;", sid.c_str());
157
curl_easy_setopt(handle, CURLOPT_COOKIE, cookie.c_str());
160
bool googlereader_api::mark_all_read(const std::string& feedurl) {
161
std::string real_feedurl = feedurl.substr(strlen(GREADER_FEED_PREFIX), feedurl.length() - strlen(GREADER_FEED_PREFIX));
162
std::vector<std::string> elems = utils::tokenize(real_feedurl, "?");
163
real_feedurl = utils::unescape_url(elems[0]);
164
std::string token = get_new_token();
166
std::string postcontent = utils::strprintf("s=%s&T=%s", real_feedurl.c_str(), token.c_str());
168
std::string result = post_content(GREADER_API_MARK_ALL_READ_URL, postcontent);
170
return result == "OK";
173
bool googlereader_api::mark_article_read(const std::string& guid, bool read) {
174
std::string token = get_new_token();
175
std::string postcontent;
178
postcontent = utils::strprintf("i=%s&a=user/-/state/com.google/read&r=user/-/state/com.google/kept-unread&ac=edit&T=%s", guid.c_str(), token.c_str());
180
postcontent = utils::strprintf("i=%s&r=user/-/state/com.google/read&a=user/-/state/com.google/kept-unread&a=user/-/state/com.google/tracking-kept-unread&ac=edit&T=%s", guid.c_str(), token.c_str());
183
std::string result = post_content(GREADER_API_EDIT_TAG_URL, postcontent);
185
LOG(LOG_DEBUG, "googlereader_api::mark_article_read: postcontent = %s result = %s", postcontent.c_str(), result.c_str());
187
return result == "OK";
190
std::string googlereader_api::get_new_token() {
191
CURL * handle = curl_easy_init();
194
utils::set_common_curl_options(handle, cfg);
195
configure_handle(handle);
196
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_write_data);
197
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &result);
198
curl_easy_setopt(handle, CURLOPT_URL, GREADER_API_TOKEN_URL);
199
curl_easy_perform(handle);
200
curl_easy_cleanup(handle);
202
LOG(LOG_DEBUG, "googlereader_api::get_new_token: token = %s", result.c_str());
207
bool googlereader_api::update_article_flags(const std::string& oldflags, const std::string& newflags, const std::string& guid) {
208
std::string star_flag = cfg->get_configvalue("googlereader-flag-star");
209
std::string share_flag = cfg->get_configvalue("googlereader-flag-share");
212
if (star_flag.length() > 0) {
213
if (strchr(oldflags.c_str(), star_flag[0])==NULL && strchr(newflags.c_str(), star_flag[0])!=NULL) {
214
success = star_article(guid, true);
215
} else if (strchr(oldflags.c_str(), star_flag[0])!=NULL && strchr(newflags.c_str(), star_flag[0])==NULL) {
216
success = star_article(guid, false);
220
if (share_flag.length() > 0) {
221
if (strchr(oldflags.c_str(), share_flag[0])==NULL && strchr(newflags.c_str(), share_flag[0])!=NULL) {
222
success = share_article(guid, true);
223
} else if (strchr(oldflags.c_str(), share_flag[0])!=NULL && strchr(newflags.c_str(), share_flag[0])==NULL) {
224
success = share_article(guid, false);
231
bool googlereader_api::star_article(const std::string& guid, bool star) {
232
std::string token = get_new_token();
233
std::string postcontent;
236
postcontent = utils::strprintf("i=%s&a=user/-/state/com.google/starred&ac=edit&T=%s", guid.c_str(), token.c_str());
238
postcontent = utils::strprintf("i=%s&r=user/-/state/com.google/starred&ac=edit&T=%s", guid.c_str(), token.c_str());
241
std::string result = post_content(GREADER_API_EDIT_TAG_URL, postcontent);
243
return result == "OK";
246
bool googlereader_api::share_article(const std::string& guid, bool share) {
247
std::string token = get_new_token();
248
std::string postcontent;
251
postcontent = utils::strprintf("i=%s&a=user/-/state/com.google/broadcast&ac=edit&T=%s", guid.c_str(), token.c_str());
253
postcontent = utils::strprintf("i=%s&r=user/-/state/com.google/broadcast&ac=edit&T=%s", guid.c_str(), token.c_str());
256
std::string result = post_content(GREADER_API_EDIT_TAG_URL, postcontent);
258
return result == "OK";
261
std::string googlereader_api::post_content(const std::string& url, const std::string& postdata) {
264
CURL * handle = curl_easy_init();
265
utils::set_common_curl_options(handle, cfg);
266
configure_handle(handle);
267
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_write_data);
268
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &result);
269
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, postdata.c_str());
270
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
271
curl_easy_perform(handle);
272
curl_easy_cleanup(handle);
274
LOG(LOG_DEBUG, "googlereader_api::post_content: url = %s postdata = %s result = %s", url.c_str(), postdata.c_str(), result.c_str());