1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4  * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
5  *
6  * - s-c folderclass callback handler functions
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26 
27 /* Global includes */
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <curl/curl.h>
31 
32 /* Claws Mail includes */
33 #include <folder.h>
34 #include <procmsg.h>
35 #include <localfolder.h>
36 #include <main.h>
37 #include <mh.h>
38 #include <xml.h>
39 #include <toolbar.h>
40 #include <prefs_common.h>
41 #include <prefs_toolbar.h>
42 #include <utils.h>
43 #include <file-utils.h>
44 
45 /* Local includes */
46 #include "libfeed/feeditem.h"
47 #include "rssyl.h"
48 #include "rssyl_deleted.h"
49 #include "rssyl_gtk.h"
50 #include "rssyl_feed.h"
51 #include "rssyl_prefs.h"
52 #include "rssyl_subscribe.h"
53 #include "rssyl_update_feed.h"
54 #include "rssyl_update_format.h"
55 #include "opml_import.h"
56 #include "opml_export.h"
57 #include "strutils.h"
58 
59 FolderClass rssyl_class;
60 
61 static gint rssyl_create_tree(Folder *folder);
62 static gint rssyl_scan_tree(Folder *folder);
63 
64 static gboolean existing_tree_found = FALSE;
65 
rssyl_init_read_func(FolderItem * item,gpointer data)66 static void rssyl_init_read_func(FolderItem *item, gpointer data)
67 {
68 	RFolderItem *ritem = (RFolderItem *)item;
69 	RPrefs *rsprefs = NULL;
70 
71 	if( !IS_RSSYL_FOLDER_ITEM(item) )
72 		return;
73 
74 	existing_tree_found = TRUE;
75 
76 	/* Don't do anything if we're on root of our folder tree or on
77 	 * a regular folder (no feed) */
78 	if( folder_item_parent(item) == NULL || ritem->url == NULL )
79 		return;
80 
81 	ritem->refresh_id = 0;
82 
83 	/* Start automatic refresh timer, if necessary */
84 	if( ritem->default_refresh_interval ) {
85 		rsprefs = rssyl_prefs_get();
86 		if( !rsprefs->refresh_enabled )
87 			return;
88 
89 		ritem->refresh_interval = rsprefs->refresh;
90 	}
91 
92 	/* Start the timer, if determined interval is >0 */
93 	if( ritem->refresh_interval > 0 )
94 		rssyl_feed_start_refresh_timeout(ritem);
95 }
96 
rssyl_make_rc_dir(void)97 static void rssyl_make_rc_dir(void)
98 {
99 	gchar *rssyl_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
100 			NULL);
101 
102 	if( !is_dir_exist(rssyl_dir) ) {
103 		if( make_dir(rssyl_dir) < 0 ) {
104 			g_warning("couldn't create directory %s", rssyl_dir);
105 		}
106 
107 		debug_print("RSSyl: created directory %s\n", rssyl_dir);
108 	}
109 
110 	g_free(rssyl_dir);
111 }
112 
rssyl_create_default_mailbox(void)113 static void rssyl_create_default_mailbox(void)
114 {
115 	Folder *root = NULL;
116 
117 	rssyl_make_rc_dir();
118 
119 	root = folder_new(rssyl_folder_get_class(), RSSYL_DEFAULT_MAILBOX, NULL);
120 
121 	g_return_if_fail(root != NULL);
122 	folder_add(root);
123 
124 	rssyl_scan_tree(root);
125 
126 	/* FIXME: subscribe default feed */
127 //	rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
128 }
129 
rssyl_update_all_feeds_deferred(gpointer data)130 static gboolean rssyl_update_all_feeds_deferred(gpointer data)
131 {
132 	rssyl_update_all_feeds();
133 	return FALSE;
134 }
135 
rssyl_toolbar_cb_refresh_all_feeds(gpointer parent,const gchar * item_name,gpointer data)136 static void rssyl_toolbar_cb_refresh_all_feeds(gpointer parent, const gchar *item_name, gpointer data)
137 {
138 	rssyl_update_all_feeds();
139 }
140 
rssyl_init(void)141 void rssyl_init(void)
142 {
143 	folder_register_class(rssyl_folder_get_class());
144 
145 	rssyl_gtk_init();
146 	rssyl_make_rc_dir();
147 
148 	rssyl_prefs_init();
149 
150 	folder_func_to_all_folders((FolderItemFunc)rssyl_init_read_func, NULL);
151 
152 	if( !existing_tree_found )
153 		rssyl_create_default_mailbox();
154 	else
155 		rssyl_update_format();
156 
157 	prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, PLUGIN_NAME, _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all_feeds, NULL);
158 
159 	if( rssyl_prefs_get()->refresh_on_startup &&
160 			!prefs_common_get_prefs()->work_offline &&
161 			claws_is_starting() )
162 		g_timeout_add(2000, rssyl_update_all_feeds_deferred, NULL);
163 }
164 
rssyl_done(void)165 void rssyl_done(void)
166 {
167 	rssyl_opml_export();
168 
169 	prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, PLUGIN_NAME, _("Refresh all feeds"));
170 
171 	rssyl_prefs_done();
172 	rssyl_gtk_done();
173 
174 	if( !claws_is_exiting() )
175 		folder_unregister_class(rssyl_folder_get_class());
176 
177 	debug_print("RSSyl is done\n");
178 }
179 
rssyl_get_new_msg_filename(FolderItem * dest)180 static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
181 {
182 	gchar *destfile;
183 	gchar *destpath;
184 
185 	destpath = folder_item_get_path(dest);
186 	g_return_val_if_fail(destpath != NULL, NULL);
187 
188 	if( !is_dir_exist(destpath) )
189 		make_dir_hier(destpath);
190 
191 	for( ; ; ) {
192 		destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
193 				dest->last_num + 1);
194 		if( is_file_entry_exist(destfile) ) {
195 			dest->last_num++;
196 			g_free(destfile);
197 		} else
198 			break;
199 	}
200 
201 	g_free(destpath);
202 
203 	return destfile;
204 }
205 
rssyl_get_last_num(Folder * folder,FolderItem * item)206 static void rssyl_get_last_num(Folder *folder, FolderItem *item)
207 {
208 	gchar *path;
209 	const char *f;
210 	GDir *dp;
211 	GError *error = NULL;
212 	gint max = 0;
213 	gint num;
214 
215 	g_return_if_fail(item != NULL);
216 
217 	debug_print("rssyl_get_last_num(): Scanning %s ...\n", item->path);
218 	path = folder_item_get_path(item);
219 	g_return_if_fail(path != NULL);
220 
221 	if( (dp = g_dir_open(path, 0, &error)) == NULL ) {
222 		FILE_OP_ERROR(item->path, "g_dir_open");
223 		debug_print("g_dir_open() failed on \"%s\", error %d (%s).\n",
224 				path, error->code, error->message);
225 		g_error_free(error);
226 		g_free(path);
227 		return;
228 	}
229 
230 	g_free(path);
231 
232 	while( (f = g_dir_read_name(dp)) != NULL) {
233 		if ((num = to_number(f)) > 0 &&
234 				g_file_test(f, G_FILE_TEST_IS_REGULAR)) {
235 			if( max < num )
236 				max = num;
237 		}
238 	}
239 	g_dir_close(dp);
240 
241 	debug_print("Last number in dir %s = %d\n", item->path, max);
242 	item->last_num = max;
243 }
244 
rssyl_new_folder(const gchar * name,const gchar * path)245 static Folder *rssyl_new_folder(const gchar *name, const gchar *path)
246 {
247 	Folder *folder;
248 
249 	debug_print("RSSyl: new_folder: %s (%s)\n", name, path);
250 
251 	rssyl_make_rc_dir();
252 
253 	folder = g_new0(Folder, 1);
254 	FOLDER(folder)->klass = &rssyl_class;
255 	folder_init(FOLDER(folder), name);
256 
257 	return FOLDER(folder);
258 }
259 
rssyl_destroy_folder(Folder * folder)260 static void rssyl_destroy_folder(Folder *folder)
261 {
262 	folder_local_folder_destroy(LOCAL_FOLDER(folder));
263 }
264 
rssyl_item_set_xml(Folder * folder,FolderItem * item,XMLTag * tag)265 static void rssyl_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
266 {
267 	GList *cur;
268 	RFolderItem *ritem = (RFolderItem *)item;
269 
270 	folder_item_set_xml(folder, item, tag);
271 
272 	for( cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
273 		XMLAttr *attr = (XMLAttr *) cur->data;
274 
275 		if( !attr || !attr->name || !attr->value)
276 			continue;
277 
278 		/* (str) URL */
279 		if( !strcmp(attr->name, "uri")) {
280 			g_free(ritem->url);
281 			ritem->url = g_strdup(attr->value);
282 		}
283 		/* (int) URL auth */
284 		if (!strcmp(attr->name, "auth")) {
285 			ritem->auth->type = atoi(attr->value);
286 		}
287 		/* (str) Auth user */
288 		if (!strcmp(attr->name, "auth_user")) {
289 			g_free(ritem->auth->username);
290 			ritem->auth->username = g_strdup(attr->value);
291 		}
292 		/* (str) Auth pass - save directly to password store */
293 		if (!strcmp(attr->name, "auth_pass")) {
294 			gsize len = 0;
295 			guchar *pwd = g_base64_decode(attr->value, &len);
296 			memset(attr->value, 0, strlen(attr->value));
297 			rssyl_passwd_set(ritem, (gchar *)pwd);
298 			memset(pwd, 0, strlen(pwd));
299 			g_free(pwd);
300 		}
301 		/* (str) Official title */
302 		if( !strcmp(attr->name, "official_title")) {
303 			g_free(ritem->official_title);
304 			ritem->official_title = g_strdup(attr->value);
305 		}
306 		/* (bool) Keep old items */
307 		if( !strcmp(attr->name, "keep_old"))
308 			ritem->keep_old = (atoi(attr->value) == 0 ? FALSE : TRUE );
309 		/* (bool) Use default refresh_interval */
310 		if( !strcmp(attr->name, "default_refresh_interval"))
311 			ritem->default_refresh_interval = (atoi(attr->value) == 0 ? FALSE : TRUE );
312 		/* (int) Refresh interval */
313 		if( !strcmp(attr->name, "refresh_interval"))
314 			ritem->refresh_interval = atoi(attr->value);
315 		/* (bool) Fetch comments */
316 		if( !strcmp(attr->name, "fetch_comments"))
317 			ritem->fetch_comments = (atoi(attr->value) == 0 ? FALSE : TRUE );
318 		/* (int) Max age of posts to fetch comments for */
319 		if( !strcmp(attr->name, "fetch_comments_max_age"))
320 			ritem->fetch_comments_max_age = atoi(attr->value);
321 		/* (bool) Write heading */
322 		if( !strcmp(attr->name, "write_heading"))
323 			ritem->write_heading = (atoi(attr->value) == 0 ? FALSE : TRUE );
324 		/* (int) Silent update */
325 		if( !strcmp(attr->name, "silent_update"))
326 			ritem->silent_update = atoi(attr->value);
327 		/* (bool) Ignore title rename */
328 		if( !strcmp(attr->name, "ignore_title_rename"))
329 			ritem->ignore_title_rename = (atoi(attr->value) == 0 ? FALSE : TRUE );
330 		/* (bool) Verify SSL peer  */
331 		if( !strcmp(attr->name, "ssl_verify_peer"))
332 			ritem->ssl_verify_peer = (atoi(attr->value) == 0 ? FALSE : TRUE );
333 	}
334 }
335 
rssyl_item_get_xml(Folder * folder,FolderItem * item)336 static XMLTag *rssyl_item_get_xml(Folder *folder, FolderItem *item)
337 {
338 	XMLTag *tag;
339 	RFolderItem *ri = (RFolderItem *)item;
340 	gchar *tmp = NULL;
341 
342 	tag = folder_item_get_xml(folder, item);
343 
344 	/* (str) URL */
345 	if( ri->url != NULL )
346 		xml_tag_add_attr(tag, xml_attr_new("uri", ri->url));
347 	/* (int) Auth */
348 	tmp = g_strdup_printf("%d", ri->auth->type);
349 	xml_tag_add_attr(tag, xml_attr_new("auth", tmp));
350 	g_free(tmp);
351 	/* (str) Auth user */
352 	if (ri->auth->username != NULL)
353 		xml_tag_add_attr(tag, xml_attr_new("auth_user", ri->auth->username));
354 	/* (str) Official title */
355 	if( ri->official_title != NULL )
356 		xml_tag_add_attr(tag, xml_attr_new("official_title", ri->official_title));
357 	/* (bool) Keep old items */
358 	xml_tag_add_attr(tag, xml_attr_new("keep_old",
359 				(ri->keep_old ? "1" : "0")) );
360 	/* (bool) Use default refresh interval */
361 	xml_tag_add_attr(tag, xml_attr_new("default_refresh_interval",
362 				(ri->default_refresh_interval ? "1" : "0")) );
363 	/* (int) Refresh interval */
364 	tmp = g_strdup_printf("%d", ri->refresh_interval);
365 	xml_tag_add_attr(tag, xml_attr_new("refresh_interval", tmp));
366 	g_free(tmp);
367 	/* (bool) Fetch comments */
368 	xml_tag_add_attr(tag, xml_attr_new("fetch_comments",
369 				(ri->fetch_comments ? "1" : "0")) );
370 	/* (int) Max age of posts to fetch comments for */
371 	tmp = g_strdup_printf("%d", ri->fetch_comments_max_age);
372 	xml_tag_add_attr(tag, xml_attr_new("fetch_comments_max_age", tmp));
373 	g_free(tmp);
374 	/* (bool) Write heading */
375 	xml_tag_add_attr(tag, xml_attr_new("write_heading",
376 				(ri->write_heading ? "1" : "0")) );
377 	/* (int) Silent update */
378 	tmp = g_strdup_printf("%d", ri->silent_update);
379 	xml_tag_add_attr(tag, xml_attr_new("silent_update", tmp));
380 	g_free(tmp);
381 	/* (bool) Ignore title rename */
382 	xml_tag_add_attr(tag, xml_attr_new("ignore_title_rename",
383 				(ri->ignore_title_rename ? "1" : "0")) );
384 	/* (bool) Verify SSL peer */
385 	xml_tag_add_attr(tag, xml_attr_new("ssl_verify_peer",
386 				(ri->ssl_verify_peer ? "1" : "0")) );
387 
388 	return tag;
389 }
390 
rssyl_scan_tree(Folder * folder)391 static gint rssyl_scan_tree(Folder *folder)
392 {
393 	g_return_val_if_fail(folder != NULL, -1);
394 
395 	folder->outbox = NULL;
396 	folder->draft = NULL;
397 	folder->queue = NULL;
398 	folder->trash = NULL;
399 
400 	debug_print("RSSyl: scanning tree\n");
401 	rssyl_create_tree(folder);
402 
403 	return 0;
404 }
405 
rssyl_create_tree(Folder * folder)406 static gint rssyl_create_tree(Folder *folder)
407 {
408 	FolderItem *rootitem;
409 	GNode *rootnode;
410 
411 	g_return_val_if_fail(folder != NULL, -1);
412 
413 	rssyl_make_rc_dir();
414 
415 	if( !folder->node ) {
416 		rootitem = folder_item_new(folder, folder->name, NULL);
417 		rootitem->folder = folder;
418 		rootnode = g_node_new(rootitem);
419 		folder->node = rootnode;
420 		rootitem->node = rootnode;
421 	}
422 
423 	debug_print("RSSyl: created new rssyl tree\n");
424 	return 0;
425 }
426 
rssyl_item_new(Folder * folder)427 static FolderItem *rssyl_item_new(Folder *folder)
428 {
429 	RFolderItem *ritem = g_new0(RFolderItem, 1);
430 
431 	ritem->url = NULL;
432 	ritem->auth = g_new0(FeedAuth, 1);
433 	ritem->auth->type = FEED_AUTH_NONE;
434 	ritem->auth->username = NULL;
435 	ritem->auth->password = NULL;
436 	ritem->official_title = NULL;
437 	ritem->source_id = NULL;
438 	ritem->items = NULL;
439 	ritem->deleted_items = NULL;
440 	ritem->keep_old = TRUE;
441 	ritem->default_refresh_interval = TRUE;
442 	ritem->refresh_interval = atoi(PREF_DEFAULT_REFRESH);
443 	ritem->fetch_comments = FALSE;
444 	ritem->fetch_comments_max_age = -1;
445 	ritem->write_heading = TRUE;
446 	ritem->fetching_comments = FALSE;
447 	ritem->silent_update = 0;
448 	ritem->last_update = 0;
449 	ritem->ignore_title_rename = FALSE;
450 	ritem->ssl_verify_peer = TRUE;
451 	ritem->feedprop = NULL;
452 	ritem->refresh_id = 0;
453 
454 	return (FolderItem *)ritem;
455 }
456 
rssyl_item_destroy(Folder * folder,FolderItem * item)457 static void rssyl_item_destroy(Folder *folder, FolderItem *item)
458 {
459 	RFolderItem *ritem = (RFolderItem *)item;
460 
461 	g_return_if_fail(ritem != NULL);
462 
463 	g_free(ritem->url);
464 	if (ritem->auth->username)
465 		g_free(ritem->auth->username);
466 	if (ritem->auth->password)
467 		g_free(ritem->auth->password);
468 	g_free(ritem->auth);
469 	g_free(ritem->official_title);
470 	g_slist_free(ritem->items);
471 
472 	/* Remove a scheduled refresh, if any */
473 	if( ritem->refresh_id != 0)
474 		g_source_remove(ritem->refresh_id);
475 
476 	g_free(ritem);
477 }
478 
rssyl_create_folder(Folder * folder,FolderItem * parent,const gchar * name)479 static FolderItem *rssyl_create_folder(Folder *folder,
480 								FolderItem *parent, const gchar *name)
481 {
482 	gchar *path = NULL, *basepath = NULL, *itempath = NULL;
483 	FolderItem *newitem = NULL;
484 
485 	g_return_val_if_fail(folder != NULL, NULL);
486 	g_return_val_if_fail(parent != NULL, NULL);
487 	g_return_val_if_fail(name != NULL, NULL);
488 
489 	path = folder_item_get_path(parent);
490 	if( !is_dir_exist(path) ) {
491 		if( (make_dir_hier(path) != 0) ) {
492 			debug_print("RSSyl: Couldn't create directory (rec) '%s'\n", path);
493 			return NULL;
494 		}
495 	}
496 
497 	basepath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
498 	path = g_strconcat(path, G_DIR_SEPARATOR_S, basepath, NULL);
499 
500 	if( make_dir(path) < 0 ) {
501 		debug_print("RSSyl: Couldn't create directory '%s'\n", path);
502 		g_free(path);
503 		g_free(basepath);
504 		return NULL;
505 	}
506 	g_free(path);
507 
508 	itempath = g_strconcat((parent->path ? parent->path : ""),
509 			G_DIR_SEPARATOR_S, basepath, NULL);
510 	newitem = folder_item_new(folder, name, itempath);
511 	g_free(itempath);
512 	g_free(basepath);
513 
514 	folder_item_append(parent, newitem);
515 
516 	return newitem;
517 }
518 
rssyl_get_root_folderitem(FolderItem * item)519 FolderItem *rssyl_get_root_folderitem(FolderItem *item)
520 {
521 	FolderItem *i;
522 
523 	for( i = item; folder_item_parent(i) != NULL; i = folder_item_parent(i) ) { }
524 	return i;
525 }
526 
rssyl_item_get_path(Folder * folder,FolderItem * item)527 static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
528 {
529 	gchar *path, *name;
530 
531 	g_return_val_if_fail(folder != NULL, NULL);
532 	g_return_val_if_fail(item != NULL, NULL);
533 
534 	name = folder_item_get_name(rssyl_get_root_folderitem(item));
535 	path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
536 			G_DIR_SEPARATOR_S, name, item->path, NULL);
537 	g_free(name);
538 
539 	return path;
540 }
541 
rssyl_rename_folder_func(GNode * node,gpointer data)542 static gboolean rssyl_rename_folder_func(GNode *node, gpointer data)
543 {
544 	FolderItem *item = node->data;
545 	gchar **paths = data;
546 	const gchar *oldpath = paths[0];
547 	const gchar *newpath = paths[1];
548 	gchar *base;
549 	gchar *new_itempath;
550 	gint oldpathlen;
551 
552 	oldpathlen = strlen(oldpath);
553 	if (strncmp(oldpath, item->path, oldpathlen) != 0) {
554 		g_warning("path doesn't match: %s, %s", oldpath, item->path);
555 		return TRUE;
556 	}
557 
558 	base = item->path + oldpathlen;
559 	while (*base == G_DIR_SEPARATOR) base++;
560 	if (*base == '\0')
561 		new_itempath = g_strdup(newpath);
562 	else
563 		new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
564 				NULL);
565 	g_free(item->path);
566 	item->path = new_itempath;
567 
568 	return FALSE;
569 }
570 
rssyl_rename_folder(Folder * folder,FolderItem * item,const gchar * name)571 static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
572 				const gchar *name)
573 {
574 	gchar *oldpath;
575 	gchar *dirname;
576 	gchar *newpath, *utf8newpath;
577 	gchar *basenewpath;
578 	gchar *paths[2];
579 
580 	g_return_val_if_fail(folder != NULL, -1);
581 	g_return_val_if_fail(item != NULL, -1);
582 	g_return_val_if_fail(item->path != NULL, -1);
583 	g_return_val_if_fail(name != NULL, -1);
584 
585 	debug_print("RSSyl: rssyl_rename_folder '%s' -> '%s'\n",
586 			item->name, name);
587 
588 	if (!strcmp(item->name, name))
589 			return 0;
590 
591 	oldpath = folder_item_get_path(item);
592 	if( !is_dir_exist(oldpath) )
593 		make_dir_hier(oldpath);
594 
595 	dirname = g_path_get_dirname(oldpath);
596 	basenewpath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
597 	newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, basenewpath, NULL);
598 	g_free(dirname);
599 	g_free(basenewpath);
600 
601 	if( g_rename(oldpath, newpath) < 0 ) {
602 		FILE_OP_ERROR(oldpath, "rename");
603 		g_free(oldpath);
604 		g_free(newpath);
605 		return -1;
606 	}
607 
608 	g_free(oldpath);
609 	g_free(newpath);
610 
611 	if( strchr(item->path, G_DIR_SEPARATOR) != NULL ) {
612 		dirname = g_path_get_dirname(item->path);
613 		utf8newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
614 		g_free(dirname);
615 	} else
616 		utf8newpath = g_strdup(name);
617 
618 	g_free(item->name);
619 	item->name = g_strdup(name);
620 
621 	paths[0] = g_strdup(item->path);
622 	paths[1] = utf8newpath;
623 	g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
624 			rssyl_rename_folder_func, paths);
625 
626 	g_free(paths[0]);
627 	g_free(paths[1]);
628 
629 	return 0;
630 }
631 
rssyl_remove_folder(Folder * folder,FolderItem * item)632 static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
633 {
634 	gchar *path = NULL;
635 	RFolderItem *ritem = (RFolderItem *)item;
636 
637 	g_return_val_if_fail(folder != NULL, -1);
638 	g_return_val_if_fail(item != NULL, -1);
639 	g_return_val_if_fail(item->path != NULL, -1);
640 	g_return_val_if_fail(item->stype == F_NORMAL, -1);
641 
642 	debug_print("RSSyl: removing folder item %s\n", item->path);
643 
644 	path = folder_item_get_path(item);
645 	if( remove_dir_recursive(path) < 0 ) {
646 		g_warning("can't remove directory '%s'", path);
647 		g_free(path);
648 		return -1;
649 	}
650 	g_free(path);
651 
652 	if (ritem->url != NULL)
653 		rssyl_passwd_set(ritem, NULL);
654 
655 	folder_item_remove(item);
656 
657 	return 0;
658 }
659 
rssyl_get_num_list(Folder * folder,FolderItem * item,MsgNumberList ** list,gboolean * old_uids_valid)660 static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
661 		MsgNumberList **list, gboolean *old_uids_valid)
662 {
663 	gchar *path;
664 	GDir *dp;
665 	const gchar *d;
666 	GError *error = NULL;
667 	gint num, nummsgs = 0;
668 
669 	g_return_val_if_fail(item != NULL, -1);
670 
671 	debug_print("RSSyl: get_num_list: scanning '%s'\n", item->path);
672 
673 	*old_uids_valid = TRUE;
674 
675 	path = folder_item_get_path(item);
676 	g_return_val_if_fail(path != NULL, -1);
677 
678 	if( (dp = g_dir_open(path, 0, &error)) == NULL ) {
679 		debug_print("g_dir_open() failed on \"%s\", error %d (%s).\n",
680 				path, error->code, error->message);
681 		g_error_free(error);
682 		g_free(path);
683 		return -1;
684 	}
685 
686 	g_free(path);
687 
688 	while( (d = g_dir_read_name(dp)) != NULL ) {
689 		if( (num = to_number(d)) > 0 ) {
690 			*list = g_slist_prepend(*list, GINT_TO_POINTER(num));
691 			nummsgs++;
692 		}
693 	}
694 	g_dir_close(dp);
695 
696 	debug_print("RSSyl: get_num_list: returning %d\n", nummsgs);
697 
698 	return nummsgs;
699 }
700 
rssyl_is_msg_changed(Folder * folder,FolderItem * item,MsgInfo * msginfo)701 static gboolean rssyl_is_msg_changed(Folder *folder, FolderItem *item,
702 		MsgInfo *msginfo)
703 {
704 	GStatBuf s;
705 	gchar *path = NULL;
706 	gchar *itempath = NULL;
707 
708 	g_return_val_if_fail(folder != NULL, FALSE);
709 	g_return_val_if_fail(item != NULL, FALSE);
710 	g_return_val_if_fail(msginfo != NULL, FALSE);
711 
712 	itempath = folder_item_get_path(item);
713 	path = g_strconcat(itempath, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
714 	g_free(itempath);
715 
716 	if (g_stat(path, &s) < 0 ||
717 		msginfo->size != s.st_size || (
718 				(msginfo->mtime - s.st_mtime != 0) &&
719 				(msginfo->mtime - s.st_mtime != 3600) &&
720 				(msginfo->mtime - s.st_mtime != -3600))) {
721 		g_free(path);
722 		return TRUE;
723 	}
724 
725 	g_free(path);
726 	return FALSE;
727 }
728 
rssyl_fetch_msg(Folder * folder,FolderItem * item,gint num)729 static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
730 {
731 	gchar *path;
732 	gchar *file;
733 
734 	g_return_val_if_fail(item != NULL, NULL);
735 	g_return_val_if_fail(num > 0, NULL);
736 
737 	path = folder_item_get_path(item);
738 	file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
739 	g_free(path);
740 
741 	debug_print("RSSyl: fetch_msg '%s'\n", file);
742 
743 	if( !is_file_exist(file)) {
744 		g_free(file);
745 		return NULL;
746 	}
747 
748 	return file;
749 }
750 
rssyl_get_msginfo(Folder * folder,FolderItem * item,gint num)751 static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
752 {
753 	MsgInfo *msginfo = NULL;
754 	gchar *file;
755 	MsgFlags flags;
756 
757 	g_return_val_if_fail(folder != NULL, NULL);
758 	g_return_val_if_fail(item != NULL, NULL);
759 	g_return_val_if_fail(num > 0, NULL);
760 
761 	debug_print("RSSyl: get_msginfo: %d\n", num);
762 
763 	file = rssyl_fetch_msg(folder, item, num);
764 	g_return_val_if_fail(file != NULL, NULL);
765 
766 	flags.perm_flags = 0;
767 	flags.tmp_flags = 0;
768 
769 	msginfo = rssyl_feed_parse_item_to_msginfo(file, flags, TRUE, TRUE, item);
770 	g_free(file);
771 
772 	if( msginfo )
773 		msginfo->msgnum = num;
774 
775 	return msginfo;
776 }
777 
rssyl_add_msgs(Folder * folder,FolderItem * dest,GSList * file_list,GHashTable * relation)778 static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
779 		GHashTable *relation)
780 {
781 	gchar *destfile;
782 	GSList *cur;
783 	MsgFileInfo *fileinfo;
784 
785 	g_return_val_if_fail(dest != NULL, -1);
786 	g_return_val_if_fail(file_list != NULL, -1);
787 
788 	if( dest->last_num < 0 ) {
789 		rssyl_get_last_num(folder, dest);
790 		if( dest->last_num < 0 ) return -1;
791 	}
792 
793 	for( cur = file_list; cur != NULL; cur = cur->next ) {
794 		fileinfo = (MsgFileInfo *)cur->data;
795 
796 		destfile = rssyl_get_new_msg_filename(dest);
797 		g_return_val_if_fail(destfile != NULL, -1);
798 		debug_print("RSSyl: add_msgs: new filename is '%s'\n", destfile);
799 
800 		if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
801 			g_warning("can't copy message %s to %s", fileinfo->file, destfile);
802 			g_free(destfile);
803 			return -1;
804 		}
805 
806 		if( relation != NULL )
807 			g_hash_table_insert(relation, fileinfo->msginfo != NULL ?
808 					(gpointer) fileinfo->msginfo : (gpointer) fileinfo,
809 					GINT_TO_POINTER(dest->last_num + 1));
810 		g_free(destfile);
811 		dest->last_num++;
812 	}
813 
814 
815 	return dest->last_num;
816 }
817 
rssyl_add_msg(Folder * folder,FolderItem * dest,const gchar * file,MsgFlags * flags)818 gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
819 		MsgFlags *flags)
820 {
821 	GSList file_list;
822 	MsgFileInfo fileinfo;
823 
824 	g_return_val_if_fail(file != NULL, -1);
825 
826 	fileinfo.msginfo = NULL;
827 	fileinfo.file = (gchar *)file;
828 	fileinfo.flags = flags;
829 	file_list.data = &fileinfo;
830 	file_list.next = NULL;
831 
832 	return rssyl_add_msgs(folder, dest, &file_list, NULL);
833 }
834 
rssyl_remove_msg(Folder * folder,FolderItem * item,gint num)835 static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
836 {
837 	gboolean need_scan = FALSE;
838 	gchar *file, *tmp;
839 	RFolderItem *ritem = (RFolderItem *)item;
840 
841 	g_return_val_if_fail(item != NULL, -1);
842 
843 	file = rssyl_fetch_msg(folder, item, num);
844 	g_return_val_if_fail(file != NULL, -1);
845 
846 	need_scan = mh_get_class()->scan_required(folder, item);
847 
848 	/* are we doing a folder move ? */
849 	tmp = g_strdup_printf("%s.tmp", file);
850 	if (is_file_exist(tmp)) {
851 		g_unlink(tmp);
852 		g_free(tmp);
853 		g_free(file);
854 		return 0;
855 	}
856 	g_free(tmp);
857 
858 	rssyl_deleted_update(ritem);
859 	rssyl_deleted_add(ritem, file);
860 	rssyl_deleted_store(ritem);
861 	rssyl_deleted_free(ritem);
862 
863 	if( g_unlink(file) < 0 ) {
864 		FILE_OP_ERROR(file, "unlink");
865 		g_free(file);
866 		return -1;
867 	}
868 
869 	if( !need_scan )
870 		item->mtime = time(NULL);
871 
872 	g_free(file);
873 	return 0;
874 }
875 
rssyl_remove_msgs(Folder * folder,FolderItem * item,MsgInfoList * msglist,GHashTable * relation)876 static gint rssyl_remove_msgs(Folder *folder, FolderItem *item,
877 		MsgInfoList *msglist, GHashTable *relation)
878 {
879 	gboolean need_scan = FALSE;
880 	MsgInfoList *cur;
881 	gint processed = 0;
882 
883 	RFolderItem *ritem = (RFolderItem *)item;
884 
885 	g_return_val_if_fail(item != NULL, -1);
886 
887 	need_scan = mh_get_class()->scan_required(folder, item);
888 
889 	rssyl_deleted_update(ritem);
890 
891 	for (cur = msglist; cur != NULL; cur = cur->next) {
892 		gchar *file;
893 		MsgInfo *msginfo = (MsgInfo *)cur->data;
894 
895 		if (msginfo == NULL)
896 			continue;
897 
898 		file = rssyl_fetch_msg(folder, item, msginfo->msgnum);
899 		if (file == NULL)
900 			continue;
901 
902 		rssyl_deleted_add(ritem, file);
903 
904 		if (claws_unlink(file) < 0) {
905 			FILE_OP_ERROR(file, "unlink");
906 		}
907 
908 		g_free(file);
909 		processed++;
910 	}
911 
912 	if (processed > 0)
913 		rssyl_deleted_store(ritem);
914 	rssyl_deleted_free(ritem);
915 
916 	if (!need_scan)
917 		item->mtime = time(NULL);
918 
919 	return processed;
920 }
921 
rssyl_subscribe_uri(Folder * folder,const gchar * uri)922 static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
923 {
924 	if (folder->klass != rssyl_folder_get_class())
925 		return FALSE;
926 	return (rssyl_subscribe(FOLDER_ITEM(folder->node->data), uri, 0) ?
927 			TRUE : FALSE);
928 }
929 
rssyl_copy_private_data(Folder * folder,FolderItem * oldi,FolderItem * newi)930 static void rssyl_copy_private_data(Folder *folder, FolderItem *oldi,
931 		FolderItem *newi)
932 {
933 	gchar *dpathold, *dpathnew;
934 	RFolderItem *olditem = (RFolderItem *)oldi,
935 									*newitem = (RFolderItem *)newi;
936 
937 	g_return_if_fail(folder != NULL);
938 	g_return_if_fail(olditem != NULL);
939 	g_return_if_fail(newitem != NULL);
940 
941 	if (olditem->url != NULL) {
942 		g_free(newitem->url);
943 		newitem->url = g_strdup(olditem->url);
944 	}
945 
946 	if (olditem->auth != NULL) {
947 		if (newitem->auth != NULL) {
948 			if (newitem->auth->username != NULL) {
949 				g_free(newitem->auth->username);
950 				newitem->auth->username = NULL;
951 			}
952 			if (newitem->auth->password != NULL) {
953 				g_free(newitem->auth->password);
954 				newitem->auth->password = NULL;
955 			}
956 			g_free(newitem->auth);
957 		}
958 		newitem->auth = g_new0(FeedAuth, 1);
959 		newitem->auth->type = olditem->auth->type;
960 		if (olditem->auth->username != NULL)
961 			newitem->auth->username = g_strdup(olditem->auth->username);
962 		if (olditem->auth->password != NULL)
963 			newitem->auth->password = g_strdup(olditem->auth->password);
964 	}
965 
966 	if (olditem->official_title != NULL) {
967 		g_free(newitem->official_title);
968 		newitem->official_title = g_strdup(olditem->official_title);
969 	}
970 
971 	if (olditem->source_id != NULL) {
972 		g_free(newitem->source_id);
973 		newitem->source_id = g_strdup(olditem->source_id);
974 	}
975 
976 	newitem->keep_old = olditem->keep_old;
977 	newitem->default_refresh_interval = olditem->default_refresh_interval;
978 	newitem->refresh_interval = olditem->refresh_interval;
979 	newitem->fetch_comments = olditem->fetch_comments;
980 	newitem->fetch_comments_max_age = olditem->fetch_comments_max_age;
981 	newitem->silent_update = olditem->silent_update;
982 	newitem->write_heading = olditem->write_heading;
983 	newitem->ignore_title_rename = olditem->ignore_title_rename;
984 	newitem->ssl_verify_peer = olditem->ssl_verify_peer;
985 	newitem->refresh_id = olditem->refresh_id;
986 	newitem->fetching_comments = olditem->fetching_comments;
987 	newitem->last_update = olditem->last_update;
988 
989 	dpathold = g_strconcat(rssyl_item_get_path(oldi->folder, oldi),
990 			G_DIR_SEPARATOR_S, RSSYL_DELETED_FILE, NULL);
991 	dpathnew = g_strconcat(rssyl_item_get_path(newi->folder, newi),
992 			G_DIR_SEPARATOR_S, RSSYL_DELETED_FILE, NULL);
993 	move_file(dpathold, dpathnew, TRUE);
994 	g_free(dpathold);
995 	g_free(dpathnew);
996 
997 }
998 
999 /************************************************************************/
1000 
rssyl_folder_get_class()1001 FolderClass *rssyl_folder_get_class()
1002 {
1003 	if( rssyl_class.idstr == NULL ) {
1004 		rssyl_class.type = F_UNKNOWN;
1005 		rssyl_class.idstr = "rssyl";
1006 		rssyl_class.uistr = PLUGIN_NAME;
1007 
1008 		/* Folder functions */
1009 		rssyl_class.new_folder = rssyl_new_folder;
1010 		rssyl_class.destroy_folder = rssyl_destroy_folder;
1011 		rssyl_class.set_xml = folder_set_xml;
1012 		rssyl_class.get_xml = folder_get_xml;
1013 		rssyl_class.scan_tree = rssyl_scan_tree;
1014 		rssyl_class.create_tree = rssyl_create_tree;
1015 
1016 		/* FolderItem functions */
1017 		rssyl_class.item_new = rssyl_item_new;
1018 		rssyl_class.item_destroy = rssyl_item_destroy;
1019 		rssyl_class.item_get_path = rssyl_item_get_path;
1020 		rssyl_class.create_folder = rssyl_create_folder;
1021 		rssyl_class.rename_folder = rssyl_rename_folder;
1022 		rssyl_class.remove_folder = rssyl_remove_folder;
1023 		rssyl_class.get_num_list = rssyl_get_num_list;
1024 		rssyl_class.scan_required = mh_get_class()->scan_required;
1025 		rssyl_class.item_set_xml = rssyl_item_set_xml;
1026 		rssyl_class.item_get_xml = rssyl_item_get_xml;
1027 
1028 		/* Message functions */
1029 		rssyl_class.get_msginfo = rssyl_get_msginfo;
1030 		rssyl_class.fetch_msg = rssyl_fetch_msg;
1031 		rssyl_class.copy_msg = mh_get_class()->copy_msg;
1032 		rssyl_class.copy_msgs = mh_get_class()->copy_msgs;
1033 		rssyl_class.add_msg = rssyl_add_msg;
1034 		rssyl_class.add_msgs = rssyl_add_msgs;
1035 		rssyl_class.remove_msg = rssyl_remove_msg;
1036 		rssyl_class.remove_msgs = rssyl_remove_msgs;
1037 		rssyl_class.is_msg_changed = rssyl_is_msg_changed;
1038 //		rssyl_class.change_flags = rssyl_change_flags;
1039 		rssyl_class.change_flags = NULL;
1040 		rssyl_class.subscribe = rssyl_subscribe_uri;
1041 		rssyl_class.copy_private_data = rssyl_copy_private_data;
1042 		rssyl_class.search_msgs = folder_item_search_msgs_local;
1043 	}
1044 
1045 	return &rssyl_class;
1046 }
1047