1 /*
2  * @file node_source.h  generic node source interface
3  *
4  * Copyright (C) 2005-2014 Lars Windolf <lars.windolf@gmx.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #ifndef _NODE_SOURCE_H
22 #define _NODE_SOURCE_H
23 
24 #include <glib.h>
25 #include <gmodule.h>
26 #include "node.h"
27 #include "node_type.h"
28 #include "subscription_type.h"
29 #include "fl_sources/google_reader_api.h"
30 
31 /* Liferea allows to have different sources in the feed list. These
32    sources are called "node sources" henceforth. Node sources can
33    (but do not need to) be single instance only. Node sources do
34    provide a subtree of the feed list that can be read-only
35    or not. A node source might allow or not allow to add sub folders
36    and reorder (DnD) folder contents. A node source might allow
37    hierarchic grouping of its subtree or not. These properties
38    are determined by the node source type capability flags.
39 
40    The node source concept itself is a node type. The implementation
41    of this node type can be found in node_source.c.
42 
43    The default node source type must be capable of serving as the root
44    node for all other source types. This mean it has to ensure to load
45    all other node source instances at their insertion nodes in
46    the feed list.
47 
48    Each source type has to be able to serve user requests and is
49    responsible for keeping its feed list node's states up-to-date.
50    A source type implementation can omit all callbacks marked as
51    optional. */
52 
53 typedef enum {
54 	NODE_SOURCE_CAPABILITY_IS_ROOT			= (1<<0),	/*<< flag only for default feed list source */
55 	NODE_SOURCE_CAPABILITY_DYNAMIC_CREATION		= (1<<1),	/*<< feed list source is user created */
56 	NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST	= (1<<2),	/*<< the feed list tree of the source can be changed */
57 	NODE_SOURCE_CAPABILITY_ADD_FEED			= (1<<3),	/*<< feeds can be added to the source */
58 	NODE_SOURCE_CAPABILITY_ADD_FOLDER		= (1<<4),	/*<< folders can be added to the source */
59 	NODE_SOURCE_CAPABILITY_HIERARCHIC_FEEDLIST	= (1<<5),	/*<< the feed list tree of the source can have hierarchic folders */
60 	NODE_SOURCE_CAPABILITY_ITEM_STATE_SYNC		= (1<<6),	/*<< the item state can and should be sync'ed with remote */
61 	NODE_SOURCE_CAPABILITY_CONVERT_TO_LOCAL		= (1<<7),	/*<< node sources of this type can be converted to internal subscription lists */
62 	NODE_SOURCE_CAPABILITY_GOOGLE_READER_API	= (1<<8),	/*<< node sources of this type are Google Reader clones */
63 	NODE_SOURCE_CAPABILITY_CAN_LOGIN		= (1<<9)	/*<< node source needs login (means loginState member is to be used) */
64 } nodeSourceCapability;
65 
66 /* Node source state model */
67 typedef enum {
68 	NODE_SOURCE_STATE_NONE = 0,		/*<< no authentication tried so far */
69 	NODE_SOURCE_STATE_IN_PROGRESS,		/*<< authentication in progress */
70 	NODE_SOURCE_STATE_ACTIVE,		/*<< authentication succeeded */
71 	NODE_SOURCE_STATE_NO_AUTH,		/*<< authentication has failed */
72 	NODE_SOURCE_STATE_MIGRATE,		/*<< source will be migrated, do not do anything anymore! */
73 } nodeSourceState;
74 
75 /* Node source subscription update flags */
76 typedef enum {
77 	/*
78 	 * Update only the subscription list, and not each node underneath it.
79 	 * Note: Uses higher 16 bits to avoid conflict.
80 	 */
81 	NODE_SOURCE_UPDATE_ONLY_LIST = (1<<16),
82 	/*
83 	 * Only login, do not do any updates.
84 	 */
85 	NODE_SOURCE_UPDATE_ONLY_LOGIN = (1<<17)
86 } nodeSourceUpdate;
87 
88 /*
89  * Number of auth failures after which we stop bothering the user while
90  * auto-updating until he manually updates again.
91  */
92 #define NODE_SOURCE_MAX_AUTH_FAILURES		3
93 
94 /* feed list node source type */
95 typedef struct nodeSourceType {
96 	const gchar	*id;		/*<< a unique feed list source type identifier */
97 	const gchar	*name;		/*<< a descriptive source name (for preferences and menus) */
98 	gulong		capabilities;	/*<< bitmask of feed list source capabilities */
99 	googleReaderApi	api;		/*<< OPTIONAL endpoint definitions for Google Reader like JSON API */
100 
101 	/* The subscription type for all child nodes that are subscriptions */
102 	subscriptionTypePtr	feedSubscriptionType;
103 
104 	/* The subscription type for the source itself (can be NULL) */
105 	subscriptionTypePtr	sourceSubscriptionType;
106 
107 	/* source type loading and unloading methods */
108 	void		(*source_type_init)(void);
109 	void 		(*source_type_deinit)(void);
110 
111 	/*
112 	 * This OPTIONAL callback is used to create an instance
113 	 * of the implemented source type. It is to be called by
114 	 * the parent source node_request_add_*() implementation.
115 	 * Mandatory for all sources except the root source.
116 	 */
117 	void 		(*source_new)(void);
118 
119 	/*
120 	 * This OPTIONAL callback is used to delete an instance
121 	 * of the implemented source type. It is to be called
122 	 * by the parent source node_remove() implementation.
123 	 * Mandatory for all sources except the root provider source.
124 	 */
125 	void 		(*source_delete)(nodePtr node);
126 
127 	/*
128 	 * This MANDATORY method is called when the source is to
129 	 * create the feed list subtree attached to the source root
130 	 * node.
131 	 */
132 	void 		(*source_import)(nodePtr node);
133 
134 	/*
135 	 * This MANDATORY method is called when the source is to
136 	 * save it's feed list subtree (if necessary at all). This
137 	 * is not a request to save the data of the attached nodes!
138 	 */
139 	void 		(*source_export)(nodePtr node);
140 
141 	/*
142 	 * This MANDATORY method is called to get an OPML representation
143 	 * of the feedlist of the given node source. Returns a newly
144 	 * allocated filename string that is to be freed by the
145 	 * caller.
146 	 */
147 	gchar *		(*source_get_feedlist)(nodePtr node);
148 
149 	/*
150 	 * This MANDATARY method is called to request the source to update
151 	 * its subscriptions list and the child subscriptions according
152 	 * the its update interval.
153 	 */
154 	void		(*source_auto_update)(nodePtr node);
155 
156 	/*
157 	 * Frees all data of the given node source instance. To be called
158 	 * during node_free() for a source node.
159 	 */
160 	void		(*free) (nodePtr node);
161 
162 	/*
163 	 * Changes the flag state of an item.  This is to allow node source type
164 	 * implementations to synchronize remote item states.
165 	 *
166 	 * This is an OPTIONAL method.
167 	 */
168 	void		(*item_set_flag) (nodePtr node, itemPtr item, gboolean newState);
169 
170 	/*
171 	 * Mark an item as read. This is to allow node source type
172 	 * implementations to synchronize remote item states.
173 	 *
174 	 * This is an OPTIONAL method.
175 	 */
176 	void            (*item_mark_read) (nodePtr node, itemPtr item, gboolean newState);
177 
178 	/*
179 	 * Add a new folder to the feed list provided by node
180 	 * source. OPTIONAL, but must be implemented when
181 	 * NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST and
182 	 * NODE_SOURCE_CAPABILITY_HIERARCHIC_FEEDLIST are set.
183 	 */
184 	nodePtr		(*add_folder) (nodePtr node, const gchar *title);
185 
186 	/*
187 	 * Add a new subscription to the feed list provided
188 	 * by the node source. OPTIONAL method, that must be implemented
189 	 * when NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST is set.
190 	 *
191 	 * The implementation could propagate the added subscription
192 	 * to a remote feed list service.
193 	 *
194 	 * The implementation MUST create and return a new child node
195 	 * setup with the given subscription which might be changed as necessary.
196 	 *
197 	 * The returned node will be automatically added to the feed list UI.
198 	 * Initial update and state saving will be triggered automatically.
199 	 */
200 	nodePtr		(*add_subscription) (nodePtr node, struct subscription *subscription);
201 
202 	/*
203 	 * Removes an existing node (subscription or folder) from the feed list
204 	 * provided by the node source. OPTIONAL method that must be
205 	 * implemented when NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST is set.
206 	 */
207 	void		(*remove_node) (nodePtr node, nodePtr child);
208 
209 	/*
210 	 * Converts all subscriptions to default source subscriptions.
211 	 *
212 	 * This is an OPTIONAL method.
213 	 */
214 	void		(*convert_to_local) (nodePtr node);
215 
216 } *nodeSourceTypePtr;
217 
218 /* feed list source instance */
219 typedef struct nodeSource {
220 	nodeSourceTypePtr	type;		/*<< node source type of this source instance */
221 	nodePtr			root;		/*<< insertion node of this node source instance */
222 	GQueue			*actionQueue;	/*<< queue for async actions */
223 	gint			loginState;	/*<< The current login state */
224 
225 	gchar			*authToken;	/*<< The authorization token */
226 	gint			authFailures;	/*<< Number of authentication failures */
227 } *nodeSourcePtr;
228 
229 /* Use this to cast the node source type from a node structure. */
230 #define NODE_SOURCE_TYPE(node) ((nodeSourcePtr)(node->source))->type
231 
232 #define NODE_SOURCE_TYPE_DUMMY_ID "fl_dummy"
233 
234 /**
235  * node_source_root_from_node: (skip)
236  * @node:	any child node
237  *
238  * Get the root node of a feed list source for any given child node.
239  *
240  * Returns: node source root node
241  */
242 nodePtr node_source_root_from_node (nodePtr node);
243 
244 /**
245  * node_source_setup_root: (skip)
246  *
247  * Scans the source type list for the root source provider.
248  * If found creates a new root source and starts it's import.
249  *
250  * Returns: a newly created root node
251  */
252 nodePtr node_source_setup_root (void);
253 
254 /**
255  * node_source_new: (skip)
256  * @node:			a newly created node
257  * @nodeSourceType:     	the node source type
258  * @url:			subscription URL
259  *
260  * Creates a new source and assigns it to the given new node.
261  * To be used to prepare a source node before adding it to the
262  * feed list. This method takes care of setting the proper source
263  * subscription type and setting up the subscription if url != NULL.
264  * The caller needs set additional auth info for the subscription.
265  */
266 void node_source_new (nodePtr node, nodeSourceTypePtr nodeSourceType, const gchar *url);
267 
268 /**
269  * node_source_set_state: (skip)
270  * @node:		the node source node
271  * @newState:		the new state
272  *
273  * Change state of the node source by node
274  */
275 void node_source_set_state (nodePtr node, gint newState);
276 
277 /**
278  * node_source_set_auth_token: (skip)
279  * @node:			a node
280  * @token:			a string
281  *
282  * Store any type of authentication token (e.g. a cookie or session id)
283  *
284  * FIXME: maybe drop this in favour of node metadata
285  */
286 void node_source_set_auth_token (nodePtr node, gchar *token);
287 
288 /**
289  * node_source_update: (skip)
290  * @node:			the source node
291  *
292  * Force the source to update its subscription list and
293  * the child subscriptions themselves.
294  */
295 void node_source_update (nodePtr node);
296 
297 /**
298  * node_source_auto_update: (skip)
299  * @node:			the source node
300  *
301  * Request the source to update its subscription list and
302  * the child subscriptions if necessary according to the
303  * update interval of the source.
304  */
305 void node_source_auto_update (nodePtr node);
306 
307 /**
308  * node_source_add_subscription: (skip)
309  * @node:		the source node
310  * @subscription:	the new subscription
311  *
312  * Called when a new subscription has been added to the node source.
313  *
314  * Returns: a new node intialized with the new subscription
315  */
316 nodePtr node_source_add_subscription (nodePtr node, struct subscription *subscription);
317 
318 /**
319  * node_source_remove_node: (skip)
320  * @node:		the source node
321  * @child:		the child node to remove
322  *
323  * Called when an existing subscription is to be removed from a node source.
324  */
325 void node_source_remove_node (nodePtr node, nodePtr child);
326 
327 /**
328  * node_source_add_folder: (skip)
329  * @node:		the source node
330  * @title:      	the folder title
331  *
332  * Called when a new folder is to be added to a node source feed list.
333  *
334  * Returns: a new node representing the new folder
335  */
336 nodePtr node_source_add_folder (nodePtr node, const gchar *title);
337 
338 /**
339  * node_source_update_folder: (skip)
340  * @node:		any node
341  * @folder:     	the target folder
342  *
343  * Called to update a nodes folder. If current folder != given folder
344  * the node will be reparented.
345  */
346 void node_source_update_folder (nodePtr node, nodePtr folder);
347 
348 /**
349  * node_source_find_or_create_folder: (skip)
350  * @parent:     	Parent folder (or source root node)
351  * @id: 		Folder/category id (or NULL)
352  * @label:		Folder display name
353  *
354  * Find a folder by the name under parent or create it.
355  *
356  * If a node source doesn't provide ids the category display name should be
357  * used as id. The worst thing happening then is to evenly named categories
358  * being merged into one (which the user can easily workaround by renaming
359  * on the remote side).
360  *
361  * Returns: a valid nodePtr
362  */
363 nodePtr node_source_find_or_create_folder (nodePtr parent, const gchar *id, const gchar *label);
364 
365 /**
366  * node_source_item_mark_read: (skip)
367  * @node:		the source node
368  * @item:		the affected item
369  * @newState:   	the new item read state
370  *
371  * Called when the read state of an item changes.
372  */
373 void node_source_item_mark_read (nodePtr node, itemPtr item, gboolean newState);
374 
375 /**
376  * node_source_set_item_flag: (skip)
377  * @node:		the source node
378  * @item:		the affected item
379  * @newState:   	the new item flag state
380  *
381  * Called when the flag state of an item changes.
382  */
383 void node_source_item_set_flag (nodePtr node, itemPtr item, gboolean newState);
384 
385 /**
386  * node_source_convert_to_local: (skip)
387  * @node:		the source node
388  *
389  * Converts all subscriptions to default source subscriptions.
390  */
391 void node_source_convert_to_local (nodePtr node);
392 
393 /**
394  * node_source_type_register: (skip)
395  * @type:		the type to register
396  *
397  * Registers a new node source type. Needs to be called before feed list import!
398  * To be used only via NodeSourceTypeActivatable
399  */
400 gboolean node_source_type_register (nodeSourceTypePtr type);
401 
402 
403 /* implementation of the node type interface */
404 
405 #define IS_NODE_SOURCE(node) (node->type == node_source_get_node_type ())
406 
407 /**
408  * node_source_get_node_type: (skip)
409  *
410  * Returns: the node source node type implementation.
411  */
412 nodeTypePtr node_source_get_node_type (void);
413 
414 #endif
415