~ubuntu-branches/ubuntu/vivid/liferea/vivid-proposed

« back to all changes in this revision

Viewing changes to src/fl_sources/google_source_opml.c

  • Committer: Package Import Robot
  • Author(s): Moray Allan, Bojo42, Rodrigo Gallardo, Moray Allan
  • Date: 2012-03-27 21:44:42 UTC
  • mfrom: (1.5.1)
  • mto: (3.3.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 122.
  • Revision ID: package-import@ubuntu.com-20120327214442-g0xfh714cdmsbnts
Tags: 1.8.3-0.1
[ Bojo42 ]
* Non-maintainer upload.
* New upstream release (Closes: #502307, #623619, #631778, #651913) 
* debian/patches:
  - drop libnotify0.7 as in upstream
  - debian-example-feeds: update, move planets from "News" to "Open Source"
  - www-browser: update due to new file location
  - libtool-dont-rearange-as-needed: rebase
* debian/control:
  - update Standards-Version
  - remove obsolete Build-Depends:
    - quilt not needed for "3.0 (quilt)" source format
    - libnm-glib-dev & libdbus-glib-1-dev: upstream switched to GDBus
    - liblua5.1-0-dev: LUA scripting support got dropped
  - new Build-Depends on libunique-dev, libjson-glib-dev & dh_autoreconf
  - update version dependencies
* debian/rules: run dh_autoreconf & update translations
* debian/liferea.install: nothing to ship from /usr/lib/liferea

[ Rodrigo Gallardo ]
* Lintian love:
  - debian/control: switch from Conflicts to Breaks
  - debian/rules: redo build targets

[ Moray Allan ]
* debian/copyright: update to include additional copyright owners.
* debian/patches/www-browser: also set default external browser.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * @file google_source_opml.c  Google reader OPML handling routines.
3
3
 * 
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>
5
7
 *
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
26
28
#include <string.h>
27
29
 
28
30
#include "common.h"
29
 
#include "conf.h"
30
31
#include "debug.h"
31
32
#include "feedlist.h"
 
33
#include "folder.h"
32
34
#include "metadata.h"
33
35
#include "node.h"
34
36
#include "subscription.h"
42
44
 * Find a node by the source id.
43
45
 */
44
46
nodePtr
45
 
google_source_get_node_by_source (GoogleSourcePtr gsource, const gchar *source) 
46
 
{
47
 
        nodePtr node;
48
 
        GSList  *iter = gsource->root->children;
 
47
google_source_opml_get_node_by_source (GoogleSourcePtr gsource, const gchar *source) 
 
48
{
 
49
        return google_source_opml_get_subnode_by_node (gsource->root, source);
 
50
}
 
51
 
 
52
/**
 
53
 * Recursively find a node by the source id.
 
54
 */
 
55
nodePtr
 
56
google_source_opml_get_subnode_by_node (nodePtr node, const gchar *source) 
 
57
{
 
58
        nodePtr subnode;
 
59
        nodePtr subsubnode;
 
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))
52
 
                        return node;
 
62
                subnode = (nodePtr)iter->data;
 
63
                if (subnode->subscription
 
64
                    && g_str_equal (subnode->subscription->source, source))
 
65
                        return subnode;
 
66
                else if (subnode->type->capabilities
 
67
                         & NODE_CAPABILITY_SUBFOLDERS) {
 
68
                        subsubnode = google_source_opml_get_subnode_by_node(subnode, source);
 
69
                        if (subnode != NULL)
 
70
                                return subsubnode;
 
71
                }
53
72
        }
54
73
        return NULL;
55
74
}
64
83
        GSList * iter = NULL; 
65
84
        nodePtr node; 
66
85
 
67
 
        iter = gsource->root->children; 
68
 
        while (iter) { 
 
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) 
71
89
                        continue;
72
90
                if (g_str_equal (node->subscription->source, GOOGLE_READER_BROADCAST_FRIENDS_URL)) {
73
91
                        return;
74
92
                }
75
 
                iter = g_slist_next (iter);
76
93
        }
77
94
 
78
95
        /* aha! add it! */
96
113
static void
97
114
google_source_check_for_removal (nodePtr node, gpointer user_data)
98
115
{
99
 
        gchar           *expr = NULL;
 
116
        gchar   *expr = NULL;
100
117
 
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)) 
102
119
                return ; 
103
120
 
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);
106
126
        } else {
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...");
108
128
                return;
109
129
        }
110
130
        
117
137
        g_free (expr);
118
138
}
119
139
 
 
140
/* 
 
141
 * Find a node by the name under root or create it.
 
142
 */
 
143
static nodePtr
 
144
google_source_find_or_create_folder (const gchar *name, nodePtr root)
 
145
{
 
146
        nodePtr         folder = NULL;
 
147
        GSList          *iter_parent;
 
148
 
 
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;
 
154
                        break;
 
155
                }
 
156
                iter_parent = g_slist_next (iter_parent);
 
157
        }
 
158
        
 
159
        /* if not found, create new folder */
 
160
        if (!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);
 
166
        }
 
167
        
 
168
        return folder;
 
169
}
 
170
 
 
171
/* 
 
172
 * Check if folder of a node changed in Google Reader and move
 
173
 * node to the folder with the same name.
 
174
 */
 
175
static void
 
176
google_source_update_folder (xmlNodePtr match, GoogleSourcePtr gsource, nodePtr node)
 
177
{
 
178
        xmlNodePtr      xml;
 
179
        xmlChar         *label;
 
180
        const gchar     *ptitle;
 
181
        nodePtr         parent;
 
182
        
 
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']");
 
187
        if (xml) {
 
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);
 
193
                }
 
194
                xmlFree (label);
 
195
        } else {
 
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);
 
199
        }
 
200
}
120
201
 
121
202
static void
122
203
google_source_merge_feed (xmlNodePtr match, gpointer user_data)
123
204
{
124
205
        GoogleSourcePtr gsource = (GoogleSourcePtr)user_data;
125
 
        nodePtr         node;
126
 
        GSList          *iter;
 
206
        nodePtr         node, parent = NULL, subnode = NULL;
 
207
        GSList          *iter, *iter_sub;
127
208
        xmlNodePtr      xml;
128
 
        xmlChar         *title = NULL, *id = NULL;
129
 
        gchar           *url = NULL;
 
209
        xmlChar         *title = NULL, *id = NULL, *label = NULL;
 
210
        gchar           *url = NULL;
130
211
 
131
212
        xml = xpath_find (match, "./string[@name='title']");
132
213
        if (xml)
135
216
        xml = xpath_find (match, "./string[@name='id']");
136
217
        if (xml) {
137
218
                id = xmlNodeListGetString (xml->doc, xml->xmlChildrenNode, 1);
138
 
                url = g_strdup(id + strlen ("feed/"));
 
219
                url = g_strdup (id + strlen ("feed/"));
139
220
        }
140
221
 
141
222
        /* Note: ids look like "feed/http://rss.slashdot.org" */
142
 
        if (id && title) {      
 
223
        if (id && title) {
143
224
 
144
225
                /* check if node to be merged already exists */
145
226
                iter = gsource->root->children;
146
227
                while (iter) {
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;
150
 
                                goto cleanup ;
 
232
                                google_source_update_folder (match, gsource, node);
 
233
                                goto cleanup;
 
234
                        } else if (node->type->capabilities
 
235
                                 & NODE_CAPABILITY_SUBFOLDERS) {
 
236
                                iter_sub = node->children;
 
237
                                while (iter_sub) {
 
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);
 
243
                                                goto cleanup;
 
244
                                        }
 
245
                                        iter_sub = g_slist_next (iter_sub);
 
246
                                }
151
247
                        }
152
248
                        iter = g_slist_next (iter);
153
249
                }
154
 
        
 
250
 
 
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']");
 
253
                if (xml) {
 
254
                        label = xmlNodeListGetString (xml->doc, xml->xmlChildrenNode, 1);
 
255
                        parent = google_source_find_or_create_folder ((gchar*)label, gsource->root);
 
256
                        xmlFree (label);
 
257
                } else {
 
258
                        parent = gsource->root;
 
259
                }
 
260
                
 
261
                g_assert (NULL != parent);
 
262
 
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);
159
267
                
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);
164
272
                
165
273
                /**
169
277
                 */
170
278
                subscription_update (node->subscription, FEED_REQ_RESET_TITLE | FEED_REQ_PRIORITY_HIGH);
171
279
                subscription_update_favicon (node->subscription);
172
 
        } else 
 
280
        } else {
173
281
                g_warning("Unable to parse subscription information from Google");
 
282
        }
174
283
 
175
284
cleanup:
176
 
        if (id)
177
 
                xmlFree (id);
178
 
        if (title)
179
 
                xmlFree (title);
 
285
        xmlFree (id);
 
286
        xmlFree (title);
180
287
        g_free (url) ;
181
288
}
182
289
 
189
296
        GoogleSourcePtr gsource = (GoogleSourcePtr) subscription->node->data;
190
297
        
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);
193
300
                if(doc) {               
194
301
                        xmlNodePtr root = xmlDocGetRootElement (doc);
195
302
                        
229
336
/** functions for an efficient updating mechanism */
230
337
 
231
338
static void
232
 
google_source_quick_update_helper (xmlNodePtr match, gpointer userdata) 
 
339
google_source_opml_quick_update_helper (xmlNodePtr match, gpointer userdata) 
233
340
{
234
341
        GoogleSourcePtr gsource = (GoogleSourcePtr) userdata;
235
342
        xmlNodePtr      xmlNode;
241
348
        id = xmlNodeGetContent (xmlNode); 
242
349
 
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);
247
354
        else {
248
355
                xmlFree (id);
249
356
                return;
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);
273
380
        }
274
381
 
275
 
        if (newestItemTimestamp) xmlFree (newestItemTimestamp);
 
382
        xmlFree (newestItemTimestamp);
276
383
        xmlFree (id);
277
384
}
278
385
 
279
386
static void
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) 
281
388
{
282
389
        GoogleSourcePtr gsource = (GoogleSourcePtr) userdata;
283
390
        xmlDocPtr       doc;
287
394
                debug0 (DEBUG_UPDATE, "GoogleSource: Unable to get unread counts, this update is aborted.");
288
395
                return;
289
396
        }
290
 
        doc = xml_parse (result->data, result->size, FALSE, NULL);
 
397
        doc = xml_parse (result->data, result->size, NULL);
291
398
        if (!doc) {
292
399
                debug0 (DEBUG_UPDATE, "GoogleSource: The XML failed to parse, maybe the session has expired. (FIXME)");
293
400
                return;
295
402
 
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);
299
406
        
300
407
        xmlFreeDoc (doc);
301
408
}
302
409
 
303
410
gboolean
304
 
google_source_quick_update(GoogleSourcePtr gsource) 
 
411
google_source_opml_quick_update(GoogleSourcePtr gsource) 
305
412
{
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);
311
418
 
312
 
        update_execute_request (gsource, request, google_source_quick_update_cb,
 
419
        update_execute_request (gsource, request, google_source_opml_quick_update_cb,
313
420
                                gsource, 0);
314
421
 
315
422
        return TRUE;
317
424
 
318
425
 
319
426
static void
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)
321
428
{
322
429
        google_subscription_opml_cb (subscription, result, flags);
323
430
}
324
431
 
325
432
static gboolean
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)
327
434
{
328
435
        GoogleSourcePtr gsource = (GoogleSourcePtr)subscription->node->data;
329
436
        
345
452
/* OPML subscription type definition */
346
453
 
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
350
457
};