2
2
* @file google_source_opml.c Google reader OPML handling routines.
4
4
* Copyright (C) 2008 Arnold Noronha <arnstein87@gmail.com>
5
* Copyright (C) 2011 Peter Oliver
6
* Copyright (C) 2011 Sergey Snitsaruk <narren96c@gmail.com>
6
8
* This program is free software; you can redistribute it and/or modify
7
9
* it under the terms of the GNU General Public License as published by
42
44
* Find a node by the source id.
45
google_source_get_node_by_source (GoogleSourcePtr gsource, const gchar *source)
48
GSList *iter = gsource->root->children;
47
google_source_opml_get_node_by_source (GoogleSourcePtr gsource, const gchar *source)
49
return google_source_opml_get_subnode_by_node (gsource->root, source);
53
* Recursively find a node by the source id.
56
google_source_opml_get_subnode_by_node (nodePtr node, const gchar *source)
60
GSList *iter = node->children;
49
61
for (; iter; iter = g_slist_next (iter)) {
50
node = (nodePtr)iter->data;
51
if (g_str_equal (node->subscription->source, source))
62
subnode = (nodePtr)iter->data;
63
if (subnode->subscription
64
&& g_str_equal (subnode->subscription->source, source))
66
else if (subnode->type->capabilities
67
& NODE_CAPABILITY_SUBFOLDERS) {
68
subsubnode = google_source_opml_get_subnode_by_node(subnode, source);
64
83
GSList * iter = NULL;
67
iter = gsource->root->children;
86
for (iter = gsource->root->children; iter; iter = g_slist_next (iter)) {
69
87
node = (nodePtr) iter->data ;
70
88
if (!node->subscription || !node->subscription->source)
72
90
if (g_str_equal (node->subscription->source, GOOGLE_READER_BROADCAST_FRIENDS_URL)) {
75
iter = g_slist_next (iter);
97
114
google_source_check_for_removal (nodePtr node, gpointer user_data)
101
if (g_str_equal (node->subscription->source, GOOGLE_READER_BROADCAST_FRIENDS_URL))
118
if (node->subscription && g_str_equal (node->subscription->source, GOOGLE_READER_BROADCAST_FRIENDS_URL))
104
121
if (IS_FEED (node)) {
105
122
expr = g_strdup_printf ("/object/list[@name='subscriptions']/object/string[@name='id'][. = 'feed/%s']", node->subscription->source);
123
} else if (IS_FOLDER (node)) {
124
node_foreach_child_data (node, google_source_check_for_removal, user_data);
125
expr = g_strdup_printf ("/object/list[@name='subscriptions']/object/list[@name='categories']/object[string='%s']", node->title);
107
g_warning ("opml_source_check_for_removal(): This should never happen...");
127
g_warning ("google_opml_source_check_for_removal(): This should never happen...");
141
* Find a node by the name under root or create it.
144
google_source_find_or_create_folder (const gchar *name, nodePtr root)
146
nodePtr folder = NULL;
149
/* find a node by the name under root */
150
iter_parent = root->children;
151
while (iter_parent) {
152
if (g_str_equal (name, node_get_title (iter_parent->data))) {
153
folder = (nodePtr)iter_parent->data;
156
iter_parent = g_slist_next (iter_parent);
159
/* if not found, create new folder */
161
folder = node_new (folder_get_node_type ());
162
node_set_title (folder, name);
163
node_set_parent (folder, root, -1);
164
feedlist_node_imported (folder);
165
subscription_update (folder->subscription, FEED_REQ_RESET_TITLE | FEED_REQ_PRIORITY_HIGH);
172
* Check if folder of a node changed in Google Reader and move
173
* node to the folder with the same name.
176
google_source_update_folder (xmlNodePtr match, GoogleSourcePtr gsource, nodePtr node)
183
/* check if label of a feed changed */
184
parent = node->parent;
185
ptitle = node_get_title (parent);
186
xml = xpath_find (match, "./list[@name='categories']/object/string[@name='label']");
188
label = xmlNodeListGetString (xml->doc, xml->xmlChildrenNode, 1);
189
if (parent == gsource->root || ! g_str_equal (label, ptitle)) {
190
debug2 (DEBUG_UPDATE, "GSource feed label changed for %s to '%s'", node->id, label);
191
parent = google_source_find_or_create_folder ((gchar*)label, gsource->root);
192
node_reparent (node, parent);
196
/* if feed has no label and parent is not gsource root, reparent to gsource root */
197
if (parent != gsource->root)
198
node_reparent (node, gsource->root);
122
203
google_source_merge_feed (xmlNodePtr match, gpointer user_data)
124
205
GoogleSourcePtr gsource = (GoogleSourcePtr)user_data;
206
nodePtr node, parent = NULL, subnode = NULL;
207
GSList *iter, *iter_sub;
128
xmlChar *title = NULL, *id = NULL;
209
xmlChar *title = NULL, *id = NULL, *label = NULL;
131
212
xml = xpath_find (match, "./string[@name='title']");
135
216
xml = xpath_find (match, "./string[@name='id']");
137
218
id = xmlNodeListGetString (xml->doc, xml->xmlChildrenNode, 1);
138
url = g_strdup(id + strlen ("feed/"));
219
url = g_strdup (id + strlen ("feed/"));
141
222
/* Note: ids look like "feed/http://rss.slashdot.org" */
144
225
/* check if node to be merged already exists */
145
226
iter = gsource->root->children;
147
228
node = (nodePtr)iter->data;
148
if (g_str_equal (node->subscription->source, url)) {
229
if (node->subscription != NULL
230
&& g_str_equal (node->subscription->source, url)) {
149
231
node->subscription->type = &googleSourceFeedSubscriptionType;
232
google_source_update_folder (match, gsource, node);
234
} else if (node->type->capabilities
235
& NODE_CAPABILITY_SUBFOLDERS) {
236
iter_sub = node->children;
238
subnode = (nodePtr)iter_sub->data;
239
if (subnode->subscription != NULL
240
&& g_str_equal (subnode->subscription->source, url)) {
241
subnode->subscription->type = &googleSourceFeedSubscriptionType;
242
google_source_update_folder (match, gsource, subnode);
245
iter_sub = g_slist_next (iter_sub);
152
248
iter = g_slist_next (iter);
251
/* if a new feed contains label, put its node under a folder with the same name */
252
xml = xpath_find (match, "./list[@name='categories']/object/string[@name='label']");
254
label = xmlNodeListGetString (xml->doc, xml->xmlChildrenNode, 1);
255
parent = google_source_find_or_create_folder ((gchar*)label, gsource->root);
258
parent = gsource->root;
261
g_assert (NULL != parent);
155
263
debug2 (DEBUG_UPDATE, "adding %s (%s)", title, url);
156
264
node = node_new (feed_get_node_type ());
157
265
node_set_title (node, title);
160
268
node_set_subscription (node, subscription_new (url, NULL, NULL));
161
269
node->subscription->type = &googleSourceFeedSubscriptionType;
162
node_set_parent (node, gsource->root, -1);
270
node_set_parent (node, parent, -1);
163
271
feedlist_node_imported (node);
189
296
GoogleSourcePtr gsource = (GoogleSourcePtr) subscription->node->data;
191
298
if (result->data) {
192
xmlDocPtr doc = xml_parse (result->data, result->size, FALSE, NULL);
299
xmlDocPtr doc = xml_parse (result->data, result->size, NULL);
194
301
xmlNodePtr root = xmlDocGetRootElement (doc);
241
348
id = xmlNodeGetContent (xmlNode);
243
350
if (g_str_has_prefix (id, "feed/"))
244
node = google_source_get_node_by_source (gsource, id + strlen ("feed/"));
351
node = google_source_opml_get_node_by_source (gsource, id + strlen ("feed/"));
245
352
else if (g_str_has_suffix (id, "broadcast-friends"))
246
node = google_source_get_node_by_source (gsource, id);
353
node = google_source_opml_get_node_by_source (gsource, id);
262
369
if (!oldNewestItemTimestamp ||
263
370
(newestItemTimestamp &&
264
371
!g_str_equal (newestItemTimestamp, oldNewestItemTimestamp))) {
265
debug3(DEBUG_UPDATE, "GoogleSource: autoupdating %s "
372
debug3(DEBUG_UPDATE, "GoogleSource: auto-updating %s "
266
373
"[oldtimestamp%s, timestamp %s]",
267
374
id, oldNewestItemTimestamp, newestItemTimestamp);
268
375
g_hash_table_insert (gsource->lastTimestampMap,
272
379
subscription_update (node->subscription, 0);
275
if (newestItemTimestamp) xmlFree (newestItemTimestamp);
382
xmlFree (newestItemTimestamp);
280
google_source_quick_update_cb (const struct updateResult* const result, gpointer userdata, updateFlags flasg)
387
google_source_opml_quick_update_cb (const struct updateResult* const result, gpointer userdata, updateFlags flags)
282
389
GoogleSourcePtr gsource = (GoogleSourcePtr) userdata;
287
394
debug0 (DEBUG_UPDATE, "GoogleSource: Unable to get unread counts, this update is aborted.");
290
doc = xml_parse (result->data, result->size, FALSE, NULL);
397
doc = xml_parse (result->data, result->size, NULL);
292
399
debug0 (DEBUG_UPDATE, "GoogleSource: The XML failed to parse, maybe the session has expired. (FIXME)");
296
403
xpath_foreach_match (xmlDocGetRootElement (doc),
297
404
"/object/list[@name='unreadcounts']/object",
298
google_source_quick_update_helper, gsource);
405
google_source_opml_quick_update_helper, gsource);
300
407
xmlFreeDoc (doc);
304
google_source_quick_update(GoogleSourcePtr gsource)
411
google_source_opml_quick_update(GoogleSourcePtr gsource)
306
413
updateRequestPtr request = update_request_new ();
307
414
request->updateState = update_state_copy (gsource->root->subscription->updateState);
309
416
update_request_set_source (request, GOOGLE_READER_UNREAD_COUNTS_URL);
310
417
update_request_set_auth_value(request, gsource->authHeaderValue);
312
update_execute_request (gsource, request, google_source_quick_update_cb,
419
update_execute_request (gsource, request, google_source_opml_quick_update_cb,
320
google_opml_subscription_process_update_result (subscriptionPtr subscription, const struct updateResult * const result, updateFlags flags)
427
google_source_opml_subscription_process_update_result (subscriptionPtr subscription, const struct updateResult * const result, updateFlags flags)
322
429
google_subscription_opml_cb (subscription, result, flags);
326
google_opml_subscription_prepare_update_request (subscriptionPtr subscription, struct updateRequest *request)
433
google_source_opml_subscription_prepare_update_request (subscriptionPtr subscription, struct updateRequest *request)
328
435
GoogleSourcePtr gsource = (GoogleSourcePtr)subscription->node->data;
345
452
/* OPML subscription type definition */
347
454
struct subscriptionType googleSourceOpmlSubscriptionType = {
348
google_opml_subscription_prepare_update_request,
349
google_opml_subscription_process_update_result
455
google_source_opml_subscription_prepare_update_request,
456
google_source_opml_subscription_process_update_result