[Commits] Makefile.am NONE 1.1.2.1 claws.def NONE 1.1.2.1 date.c NONE 1.1.2.1 date.h NONE 1.1.2.1 feed.c NONE 1.1.2.1 feed.h NONE 1.1.2.1 feedprops.c NONE 1.1.2.1 feedprops.h NONE 1.1.2.1 opml.c NONE 1.1.2.1 opml.h NONE 1.1.2.1 parsers.c NONE 1.1.2.1 parsers.h NONE 1.1.2.1 placeholder.txt 1.1.2.1 NONE plugin.c NONE 1.1.2.1 plugin.def NONE 1.1.2.1 rssyl.c NONE 1.1.2.1 rssyl.h NONE 1.1.2.1 rssyl_cb_gtk.c NONE 1.1.2.1 rssyl_cb_gtk.h NONE 1.1.2.1 rssyl_cb_menu.c NONE 1.1.2.1 rssyl_cb_menu.h NONE 1.1.2.1 rssyl_gtk.c NONE 1.1.2.1 rssyl_gtk.h NONE 1.1.2.1 rssyl_prefs.c NONE 1.1.2.1 rssyl_prefs.h NONE 1.1.2.1 strreplace.c NONE 1.1.2.1 strreplace.h NONE 1.1.2.1 version.rc NONE 1.1.2.1

colin at claws-mail.org colin at claws-mail.org
Sat Feb 16 11:35:17 CET 2013


Update of /home/claws-mail/claws/src/plugins/rssyl
In directory srv:/tmp/cvs-serv19571/src/plugins/rssyl

Added Files:
      Tag: gtk2
	Makefile.am claws.def date.c date.h feed.c feed.h feedprops.c 
	feedprops.h opml.c opml.h parsers.c parsers.h plugin.c 
	plugin.def rssyl.c rssyl.h rssyl_cb_gtk.c rssyl_cb_gtk.h 
	rssyl_cb_menu.c rssyl_cb_menu.h rssyl_gtk.c rssyl_gtk.h 
	rssyl_prefs.c rssyl_prefs.h strreplace.c strreplace.h 
	version.rc 
Removed Files:
      Tag: gtk2
	placeholder.txt 
Log Message:
2013-02-16 [colin]	3.9.0cvs70

	* src/main.c
	* src/prefs_common.c
	* src/prefs_common.h
	* src/common/defs.h
	* src/common/w32_account.c
		Add an hidden preference to set the Windows theme
	* src/plugins/Makefile.am
	* src/plugins/rssyl/Makefile.am
	* src/plugins/rssyl/claws.def
	* src/plugins/rssyl/date.c
	* src/plugins/rssyl/date.h
	* src/plugins/rssyl/feed.c
	* src/plugins/rssyl/feed.h
	* src/plugins/rssyl/feedprops.c
	* src/plugins/rssyl/feedprops.h
	* src/plugins/rssyl/opml.c
	* src/plugins/rssyl/opml.h
	* src/plugins/rssyl/parsers.c
	* src/plugins/rssyl/parsers.h
	* src/plugins/rssyl/placeholder.txt
	* src/plugins/rssyl/plugin.c
	* src/plugins/rssyl/plugin.def
	* src/plugins/rssyl/rssyl.c
	* src/plugins/rssyl/rssyl.h
	* src/plugins/rssyl/rssyl_cb_gtk.c
	* src/plugins/rssyl/rssyl_cb_gtk.h
	* src/plugins/rssyl/rssyl_cb_menu.c
	* src/plugins/rssyl/rssyl_cb_menu.h
	* src/plugins/rssyl/rssyl_gtk.c
	* src/plugins/rssyl/rssyl_gtk.h
	* src/plugins/rssyl/rssyl_prefs.c
	* src/plugins/rssyl/rssyl_prefs.h
	* src/plugins/rssyl/strreplace.c
	* src/plugins/rssyl/strreplace.h
	* src/plugins/rssyl/version.rc
		Add RSSyl
	* src/plugins/vcalendar/Makefile.am
	* src/plugins/vcalendar/Makefile.in
	* src/plugins/vcalendar/plugin.c
	* src/plugins/vcalendar/libical/libical/icalversion.h
		Fix Curl flags :)

--- NEW FILE: date.h ---
#ifndef __DATE_H
#define __DATE_H

#include <time.h>
#include <glib.h>

time_t parseISO8601Date(gchar *date);
gchar *createRFC822Date(const time_t *time);

#endif /* __DATE_H */

--- NEW FILE: feed.c ---
/*
 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005-2008 Andrej Kacian <andrej at kacian.sk> and the Claws Mail team
 *
 * - various feed parsing functions
 * - this file could use some sorting and/or splitting
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
[...1825 lines suppressed...]
		return;
	/* Don't try to update normal folders */
	if (ritem->url == NULL)
		return;
	rssyl_update_feed((RSSylFolderItem *)item);
}

void rssyl_refresh_all_feeds(void)
{
	if (prefs_common_get_prefs()->work_offline && 
	    !inc_offline_should_override(TRUE,
			ngettext("Claws Mail needs network access in order "
			    "to update the feed.",
			    "Claws Mail needs network access in order "
			    "to update the feeds.", 2))) {
			return;
	}

	folder_func_to_all_folders((FolderItemFunc)rssyl_refresh_all_func, NULL);
}

--- NEW FILE: feed.h ---
#ifndef __FEED_H
#define __FEED_H

#include <libxml/parser.h>

#include "procmsg.h"

#include "rssyl.h"

#define RSSYL_TMP_TEMPLATE	"curltmpXXXXXX"

#define RSSYL_XPATH_ROOT		"/rssyl"
#define RSSYL_XPATH_TITLE		RSSYL_XPATH_ROOT"/title"
#define RSSYL_XPATH_LINK		RSSYL_XPATH_ROOT"/link"
#define RSSYL_XPATH_TEXT		RSSYL_XPATH_ROOT"/text"

#define RSSYL_TEXT_START		"<!-- RSSyl text start -->"
#define RSSYL_TEXT_END			"<!-- RSSyl text end -->"

#define RSSYL_LOG_ERROR_TIMEOUT		_("Time out connecting to URL %s\n")
#define RSSYL_LOG_ERROR_FETCH		_("Couldn't fetch URL %s\n")
#define RSSYL_LOG_ERROR_PARSE		_("Error parsing feed from URL %s\n")
#define RSSYL_LOG_ERROR_UNKNOWN		_("Unsupported feed type at URL %s\n")

#define RSSYL_LOG_UPDATING		_("RSSyl: Updating feed %s\n")
#define RSSYL_LOG_UPDATED 		_("RSSyl: Feed update finished: %s\n")
#define RSSYL_LOG_EXITING			_("RSSyl: Feed update aborted, application is exiting.\n")

struct _RSSylFeedItemMedia {
	gchar *url;
	gchar *type;
	gulong size;
};

typedef struct _RSSylFeedItemMedia RSSylFeedItemMedia;

struct _RSSylFeedItem {
	gchar *title;
	gchar *text;
	gchar *link;
	gchar *parent_link;
	gchar *comments_link;
	gchar *author;
	gchar *id;
	gboolean id_is_permalink;

	RSSylFeedItemMedia *media;

#ifdef RSSYL_DEBUG
	long int debug_fetched;
#endif	/* RSSYL_DEBUG */

	gchar *realpath;
	time_t date;
	time_t date_published;
};

typedef struct _RSSylFeedItem RSSylFeedItem;

struct _RSSylFindByUrlCtx {
	gchar *url;
	RSSylFolderItem *ritem;
};

typedef struct _RSSylFindByUrlCtx RSSylFindByUrlCtx;

struct _RSSylParseCtx {
	RSSylFolderItem *ritem;
	gboolean ready;
};

typedef struct _RSSylParseCtx RSSylParseCtx;

xmlDocPtr rssyl_fetch_feed(const gchar *url, time_t last_update, gchar **title, gchar **error);
void rssyl_parse_feed(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent);
gboolean rssyl_add_feed_item(RSSylFolderItem *ritem, RSSylFeedItem *fitem);
MsgInfo *rssyl_parse_feed_item_to_msginfo(gchar *file, MsgFlags flags,
		gboolean a, gboolean b, FolderItem *item);
void rssyl_remove_feed_cache(FolderItem *item);
void rssyl_update_feed(RSSylFolderItem *ritem);
void rssyl_read_existing(RSSylFolderItem *ritem);

void rssyl_start_refresh_timeout(RSSylFolderItem *ritem);
void rssyl_expire_items(RSSylFolderItem *ritem);

FolderItem *rssyl_subscribe_new_feed(FolderItem *parent, const gchar *url, gboolean verbose);
void rssyl_free_feeditem(RSSylFeedItem *item);
gchar *rssyl_format_string(const gchar *str, gboolean replace_html, gboolean replace_returns);

void rssyl_refresh_all_func(FolderItem *item, gpointer data);
void rssyl_refresh_all_feeds(void);
gchar *rssyl_feed_title_to_dir(const gchar *title);

#endif /* __FEED_H */

--- placeholder.txt DELETED ---

--- NEW FILE: strreplace.h ---
#ifndef __STRREPLACE_H
#define __STRREPLACE_H

gchar *rssyl_strreplace(const gchar *source, gchar *pattern,
			gchar *replacement);
gchar *rssyl_sanitize_string(const gchar *str, gboolean strip_nl);

#endif /* __STRREPLACE_H */

--- NEW FILE: strreplace.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - a strreplace function (something like sed's s/foo/bar/g)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <glib.h>
#include <stdlib.h>
#include <ctype.h>

#include "common/utils.h"

gchar *rssyl_strreplace(const gchar *source, gchar *pattern,
			gchar *replacement)
{
	gchar *new, *w_new;
	const gchar *c;
	guint count = 0, final_length;
	size_t len_pattern, len_replacement;

/*
	debug_print("RSSyl: ======= strreplace: '%s': '%s'->'%s'\n", source, pattern,
			replacement);
*/

	if( source == NULL || pattern == NULL ) {
		debug_print("RSSyl: source or pattern is NULL!!!\n");
		return NULL;
	}

	if( !g_utf8_validate(source, -1, NULL) ) {
		debug_print("RSSyl: source is not an UTF-8 encoded text\n");
		return NULL;
	}

	if( !g_utf8_validate(pattern, -1, NULL) ) {
		debug_print("RSSyl: pattern is not an UTF-8 encoded text\n");
		return NULL;
	}

	len_pattern = strlen(pattern);
	len_replacement = strlen(replacement);

	c = source;
	while( ( c = g_strstr_len(c, strlen(c), pattern) ) ) {
		count++;
		c += len_pattern;
	}

/*
	debug_print("RSSyl: ==== count = %d\n", count);
*/

	final_length = strlen(source)
		- ( count * len_pattern )
		+ ( count * len_replacement );

	new = malloc(final_length + 1);
	w_new = new;
	memset(new, '\0', final_length + 1);

	c = source;

	while( *c != '\0' ) {
		if( !memcmp(c, pattern, len_pattern) ) {
			gboolean break_after_rep = FALSE;
			int i;
			if (*(c + len_pattern) == '\0')
				break_after_rep = TRUE;
			for (i = 0; i < len_replacement; i++) {
				*w_new = replacement[i];
				w_new++;
			}
			if (break_after_rep)
				break;
			c = c + len_pattern;
		} else {
			*w_new = *c;
			w_new++;
			c++;
		}
	}
	return new;
}

gchar *rssyl_sanitize_string(const gchar *str, gboolean strip_nl)
{
	gchar *new = NULL;
	const gchar *c = str;
	gchar *new_ptr;
	if( str == NULL )
		return NULL;

	new_ptr = new = malloc(strlen(str) + 1);
	if (new == NULL)
		return NULL;
	memset(new, '\0', strlen(str) + 1);

	while( *c != '\0' ) {
		if( !g_ascii_isspace(*c) || *c == ' ' || (!strip_nl && *c == '\n') ) {
			*new_ptr = *c;
			new_ptr++;
		}
		c++;
	}

	return new;
}

--- NEW FILE: rssyl_cb_menu.h ---
#ifndef __RSSYL_MENU_CB
#define __RSSYL_MENU_CB

#include <glib.h>
#include <gtk/gtk.h>

#include "folderview.h"

void rssyl_new_feed_cb(GtkAction *action, gpointer data);
void rssyl_remove_feed_cb(GtkAction *action, gpointer data);
void rssyl_remove_rss_cb(GtkAction *action, gpointer data);
void rssyl_refresh_cb(GtkAction *action, gpointer data);
void rssyl_prop_cb(GtkAction *action, gpointer data);
void rssyl_import_feed_list_cb(GtkAction *action, gpointer data);
void rssyl_refresh_all_cb(GtkAction *action, gpointer data);
void rssyl_rename_cb(GtkAction *action, gpointer data);
void rssyl_new_folder_cb(GtkAction *action, gpointer data);
void rssyl_remove_folder_cb(GtkAction *action, gpointer data);

#endif /* __RSSYL_MENU_FEED */

--- NEW FILE: rssyl_cb_gtk.h ---
#ifndef __RSSYL_GTK_CB
#define __RSSYL_GTK_CB

#include <glib.h>
#include <gtk/gtk.h>

gboolean rssyl_props_cancel_cb(GtkWidget *widget, gpointer data);
gboolean rssyl_props_ok_cb(GtkWidget *widget, gpointer data);
gboolean rssyl_refresh_timeout_cb(gpointer data);
gboolean rssyl_default_refresh_interval_toggled_cb(GtkToggleButton *default_ri,
		gpointer data);
gboolean rssyl_default_expired_num_toggled_cb(GtkToggleButton *default_ex,
		gpointer data);
gboolean rssyl_fetch_comments_toggled_cb(GtkToggleButton *fetch_comments,
		gpointer data);
gboolean rssyl_props_key_press_cb(GtkWidget *widget, GdkEventKey *event,
		gpointer data);

#endif /* __RSSYL_GTK_CB */

--- NEW FILE: rssyl.h ---
#ifndef __RSSYL_H
#define __RSSYL_H

#include <glib.h>

#include <folder.h>

#define PLUGIN_NAME		(_("RSSyl"))

/* Name of directory in rcdir where RSSyl will store its data. */
#define RSSYL_DIR "RSSyl"

/* Default RSSyl mailbox name */
#define RSSYL_DEFAULT_MAILBOX	_("My Feeds")

/* Default feed to be added when creating mailbox for the first time */
#define RSSYL_DEFAULT_FEED	"http://planet.claws-mail.org/rss20.xml"

struct _RSSylFolderItem {
	FolderItem item;
	GSList *contents;
	gint last_count;

	gchar *url;
	gchar *official_name;

	gboolean default_refresh_interval;
	gint refresh_interval;

	gboolean default_expired_num;
	gint expired_num;

	guint refresh_id;	
	gboolean fetch_comments;
	gint fetch_comments_for;
	gint silent_update;

	struct _RSSylFeedProp *feedprop;
};

typedef struct _RSSylFolderItem RSSylFolderItem;

struct _RSSylRefreshCtx {
	RSSylFolderItem *ritem;
	guint id;
};

typedef struct _RSSylRefreshCtx RSSylRefreshCtx;

void rssyl_init(void);
void rssyl_done(void);

FolderClass *rssyl_folder_get_class(void);

#define IS_RSSYL_FOLDER_ITEM(item) \
	(item->folder->klass == rssyl_folder_get_class())

#endif /* __RSSYL_H */

--- NEW FILE: plugin.def ---
EXPORTS
        plugin_desc
        plugin_done
        plugin_init
        plugin_licence
        plugin_name
        plugin_type
	plugin_provides
        plugin_version


--- NEW FILE: rssyl.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - s-c folderclass callback handler functions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#ifdef G_OS_WIN32
#  include <w32lib.h>
#endif

#include <glib.h>
#include <curl/curl.h>

#include "folder.h"
#include "localfolder.h"

#include "procheader.h"
#include "common/utils.h"
#include "toolbar.h"
#include "prefs_toolbar.h"

#include "main.h"

#include "feed.h"
#include "feedprops.h"
#include "opml.h"
#include "rssyl.h"
#include "rssyl_gtk.h"
#include "rssyl_prefs.h"
#include "strreplace.h"

static gint rssyl_create_tree(Folder *folder);

static gboolean existing_tree_found = FALSE;

static void rssyl_init_read_func(FolderItem *item, gpointer data)
{
	if( !IS_RSSYL_FOLDER_ITEM(item) )
		return;

	existing_tree_found = TRUE;

	if( folder_item_parent(item) == NULL )
		return;

	rssyl_get_feed_props((RSSylFolderItem *)item);
}

static void rssyl_make_rc_dir(void)
{
	gchar *rssyl_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
			NULL);

	if( !is_dir_exist(rssyl_dir) ) {
		if( make_dir(rssyl_dir) < 0 ) {
			g_warning("couldn't create directory %s\n", rssyl_dir);
		}

		debug_print("created directorty %s\n", rssyl_dir);
	}

	g_free(rssyl_dir);
}

static void rssyl_create_default_mailbox(void)
{
	Folder *root = NULL;
	FolderItem *item;

	rssyl_make_rc_dir();

	root = folder_new(rssyl_folder_get_class(), RSSYL_DEFAULT_MAILBOX, NULL);

	g_return_if_fail(root != NULL);
	folder_add(root);

	item = FOLDER_ITEM(root->node->data);

	rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
}

static gboolean rssyl_refresh_all_feeds_deferred(gpointer data)
{
	rssyl_refresh_all_feeds();
	return FALSE;
}

static void rssyl_toolbar_cb_refresh_all(gpointer parent, const gchar *item_name, gpointer data)
{
	rssyl_refresh_all_feeds();
}

void rssyl_init(void)
{
	folder_register_class(rssyl_folder_get_class());

	rssyl_gtk_init();

	rssyl_make_rc_dir();

	rssyl_prefs_init();

	folder_func_to_all_folders((FolderItemFunc)rssyl_init_read_func, NULL);

	if( existing_tree_found == FALSE )
		rssyl_create_default_mailbox();

	prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl",
			_("Refresh all feeds"), rssyl_toolbar_cb_refresh_all, NULL);

	rssyl_opml_export();

	if( rssyl_prefs_get()->refresh_on_startup &&
			claws_is_starting() )
		g_timeout_add(2000, rssyl_refresh_all_feeds_deferred, NULL);
}

void rssyl_done(void)
{
	prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl",
			_("Refresh all feeds"));
	rssyl_prefs_done();
	rssyl_gtk_done();
	if (!claws_is_exiting())
		folder_unregister_class(rssyl_folder_get_class());
}

static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
{
	gchar *destfile;
	gchar *destpath;

	destpath = folder_item_get_path(dest);
	g_return_val_if_fail(destpath != NULL, NULL);

	if( !is_dir_exist(destpath) )
		make_dir_hier(destpath);

	for( ; ; ) {
		destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
				dest->last_num + 1);
		if( is_file_entry_exist(destfile) ) {
			dest->last_num++;
			g_free(destfile);
		} else
			break;
	}

	g_free(destpath);

	return destfile;
}

static void rssyl_get_last_num(Folder *folder, FolderItem *item)
{
	gchar *path;
	DIR *dp;
	struct dirent *d;
	gint max = 0;
	gint num;

	g_return_if_fail(item != NULL);

	debug_print("rssyl_get_last_num(): Scanning %s ...\n", item->path);
	path = folder_item_get_path(item);
	g_return_if_fail(path != NULL);
	if( change_dir(path) < 0 ) {
		g_free(path);
		return;
	}
	g_free(path);

	if( (dp = opendir(".")) == NULL ) {
		FILE_OP_ERROR(item->path, "opendir");
		return;
	}

	while( (d = readdir(dp)) != NULL ) {
		if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
			if( max < num )
				max = num;
		}
	}
	closedir(dp);

	debug_print("Last number in dir %s = %d\n", item->path, max);
	item->last_num = max;
}

struct _RSSylFolder {
	LocalFolder folder;
};

typedef struct _RSSylFolder RSSylFolder;

FolderClass rssyl_class;

static Folder *rssyl_new_folder(const gchar *name, const gchar *path)
{
	RSSylFolder *folder;

	debug_print("RSSyl: new_folder\n");

	rssyl_make_rc_dir();

	folder = g_new0(RSSylFolder, 1);
	FOLDER(folder)->klass = &rssyl_class;
	folder_init(FOLDER(folder), name);

	return FOLDER(folder);
}

static void rssyl_destroy_folder(Folder *_folder)
{
	RSSylFolder *folder = (RSSylFolder *)_folder;

	folder_local_folder_destroy(LOCAL_FOLDER(folder));
}

static gint rssyl_scan_tree(Folder *folder)
{
	g_return_val_if_fail(folder != NULL, -1);

	folder->outbox = NULL;
	folder->draft = NULL;
	folder->queue = NULL;
	folder->trash = NULL;

	debug_print("RSSyl: scanning tree\n");
	rssyl_create_tree(folder);

	return 0;
}

static gint rssyl_create_tree(Folder *folder)
{
	FolderItem *rootitem;
	GNode *rootnode;

	rssyl_make_rc_dir();

	if( !folder->node ) {
		rootitem = folder_item_new(folder, folder->name, NULL);
		rootitem->folder = folder;
		rootnode = g_node_new(rootitem);
		folder->node = rootnode;
		rootitem->node = rootnode;
	} else {
		rootitem = FOLDER_ITEM(folder->node->data);
		rootnode = folder->node;
	}

	debug_print("RSSyl: created new rssyl tree\n");
	return 0;
}

static FolderItem *rssyl_item_new(Folder *folder)
{
	RSSylFolderItem *ritem;

	debug_print("RSSyl: item_new\n");

	ritem = g_new0(RSSylFolderItem, 1);

	ritem->url = NULL;
	ritem->default_refresh_interval = TRUE;
	ritem->default_expired_num = TRUE;
	ritem->fetch_comments = FALSE;
	ritem->fetch_comments_for = -1;
	ritem->silent_update = 0;
	ritem->refresh_interval = rssyl_prefs_get()->refresh;
	ritem->refresh_id = 0;
	ritem->expired_num = rssyl_prefs_get()->expired;
	ritem->last_count = 0;

	ritem->contents = NULL;
	ritem->feedprop = NULL;

	return (FolderItem *)ritem;
}

static void rssyl_item_destroy(Folder *folder, FolderItem *item)
{
	RSSylFolderItem *ritem = (RSSylFolderItem *)item;

	g_return_if_fail(ritem != NULL);

	/* Silently remove feed refresh timeouts */
	if( ritem->refresh_id != 0 )
		g_source_remove(ritem->refresh_id);

	g_free(ritem->url);
	g_free(ritem->official_name);
	g_slist_free(ritem->contents);

	g_free(item);
}

static FolderItem *rssyl_create_folder(Folder *folder,
								FolderItem *parent, const gchar *name)
{
	gchar *path = NULL, *tmp;
	FolderItem *newitem = NULL;

	g_return_val_if_fail(folder != NULL, NULL);
	g_return_val_if_fail(parent != NULL, NULL);
	g_return_val_if_fail(name != NULL, NULL);
	tmp = rssyl_feed_title_to_dir((gchar *)name);
	path = g_strconcat((parent->path != NULL) ? parent->path : "", ".",
				tmp, NULL);
	g_free(tmp);
	newitem = folder_item_new(folder, name, path);
	folder_item_append(parent, newitem);
	g_free(path);

	return newitem;
}

static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
{
	gchar *result, *tmp;
	tmp = rssyl_feed_title_to_dir(item->name);
	result = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
			G_DIR_SEPARATOR_S, tmp, NULL);
	g_free(tmp);
	return result;
}

static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
				const gchar *name)
{
	gchar *oldname = NULL, *oldpath = NULL, *newpath = NULL;
	RSSylFolderItem *ritem = NULL;
	g_return_val_if_fail(folder != NULL, -1);
	g_return_val_if_fail(item != NULL, -1);
	g_return_val_if_fail(item->path != NULL, -1);
	g_return_val_if_fail(name != NULL, -1);

	debug_print("RSSyl: renaming folder '%s' to '%s'\n", item->path, name);

	oldpath = rssyl_item_get_path(folder, item);
	
	/* now get the new path using the new name */
	oldname = item->name;
	item->name = g_strdup(name);
	newpath = rssyl_item_get_path(folder, item);
	
	/* put back the old name in case the rename fails */
	g_free(item->name);
	item->name = oldname;
	
	if (g_rename(oldpath, newpath) < 0) {
		FILE_OP_ERROR(oldpath, "rename");
		g_free(oldpath);
		g_free(newpath);
		return -1;
	}
	
	g_free(item->path);
	item->path = g_strdup_printf(".%s", name);
	
	ritem = (RSSylFolderItem *)item;
	
	if (ritem->url)
		rssyl_props_update_name(ritem, (gchar *)name);
	
	g_free(item->name);
	item->name = g_strdup(name);
	
	folder_write_list();

	return 0;
}

static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
{
	g_return_val_if_fail(folder != NULL, -1);
	g_return_val_if_fail(item != NULL, -1);
	g_return_val_if_fail(item->path != NULL, -1);
	g_return_val_if_fail(item->stype == F_NORMAL, -1);

	debug_print("RSSyl: removing folder item %s\n", item->path);

	folder_item_remove(item);

	return 0;
}

static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
		MsgNumberList **list, gboolean *old_uids_valid)
{
	gchar *path;
	DIR *dp;
	struct dirent *d;
	gint num, nummsgs = 0;
	RSSylFolderItem *ritem = (RSSylFolderItem *)item;

	g_return_val_if_fail(item != NULL, -1);

	debug_print("RSSyl: scanning '%s'...\n", item->path);

	rssyl_get_feed_props(ritem);

	if (ritem->url == NULL)
		return -1;

	*old_uids_valid = TRUE;

	path = folder_item_get_path(item);
	g_return_val_if_fail(path != NULL, -1);
	if( change_dir(path) < 0 ) {
		g_free(path);
		return -1;
	}
	g_free(path);

	if( (dp = opendir(".")) == NULL ) {
		FILE_OP_ERROR(item->path, "opendir");
		return -1;
	}

	while( (d = readdir(dp)) != NULL ) {
		if( (num = to_number(d->d_name)) > 0 ) {
			*list = g_slist_prepend(*list, GINT_TO_POINTER(num));
			nummsgs++;
		}
	}
	closedir(dp);

	return nummsgs;
}

static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
{
	return TRUE;
}

static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
{
	gchar *snum = g_strdup_printf("%d", num);
	gchar *tmp = rssyl_feed_title_to_dir(item->name);
	gchar *file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
			G_DIR_SEPARATOR_S, tmp,
			G_DIR_SEPARATOR_S, snum, NULL);
	g_free(tmp);
	debug_print("RSSyl: fetch_msg: '%s'\n", file);

	g_free(snum);

	return file;
}

static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
{
	MsgInfo *msginfo = NULL;
	gchar *file;
	MsgFlags flags;

	debug_print("RSSyl: get_msginfo: %d\n", num);

	g_return_val_if_fail(folder != NULL, NULL);
	g_return_val_if_fail(item != NULL, NULL);
	g_return_val_if_fail(num > 0, NULL);

	file = rssyl_fetch_msg(folder, item, num);
	g_return_val_if_fail(file != NULL, NULL);

	flags.perm_flags = MSG_NEW | MSG_UNREAD;
	flags.tmp_flags = 0;

	msginfo = rssyl_parse_feed_item_to_msginfo(file, flags, TRUE, TRUE, item);

	if( msginfo )
		msginfo->msgnum = num;

	g_free(file);

	return msginfo;
}

static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
		GHashTable *relation)
{
	gchar *destfile;
	GSList *cur;
	MsgFileInfo *fileinfo;

	g_return_val_if_fail(dest != NULL, -1);
	g_return_val_if_fail(file_list != NULL, -1);

	if( dest->last_num < 0 ) {
		rssyl_get_last_num(folder, dest);
		if( dest->last_num < 0 ) return -1;
	}

	for( cur = file_list; cur != NULL; cur = cur->next ) {
		fileinfo = (MsgFileInfo *)cur->data;

		destfile = rssyl_get_new_msg_filename(dest);
		g_return_val_if_fail(destfile != NULL, -1);

#ifdef G_OS_UNIX
		if( link(fileinfo->file, destfile) < 0 )
#endif
			if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
				g_warning("can't copy message %s to %s\n", fileinfo->file, destfile);
				g_free(destfile);
				return -1;
			}

		if( relation != NULL )
			g_hash_table_insert(relation, fileinfo,
					GINT_TO_POINTER(dest->last_num + 1) );
		g_free(destfile);
		dest->last_num++;
	}

	return dest->last_num;
}

static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
		MsgFlags *flags)
{
	gint ret;
	GSList file_list;
	MsgFileInfo fileinfo;

	g_return_val_if_fail(file != NULL, -1);

	fileinfo.msginfo = NULL;
	fileinfo.file = (gchar *)file;
	fileinfo.flags = flags;
	file_list.data = &fileinfo;
	file_list.next = NULL;

	ret = rssyl_add_msgs(folder, dest, &file_list, NULL);
	return ret;
}

static gint rssyl_dummy_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *info)
{
	if (info->folder == NULL || info->folder->folder != dest->folder) {
		return -1;
	}
	if (info->folder && info->folder->name && dest->name
	&&  !strcmp(info->folder->name, dest->name)) {
		/* this is a folder move */
		gchar *file = procmsg_get_message_file(info);
		gchar *tmp = g_strdup_printf("%s.tmp", file);
		copy_file(file, tmp, TRUE);
		g_free(file);
		g_free(tmp);
		return info->msgnum;
	} else {
		return -1;
	}
}
static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
{
	gboolean need_scan = FALSE;
	gchar *file, *tmp;

	g_return_val_if_fail(item != NULL, -1);

	file = rssyl_fetch_msg(folder, item, num);
	g_return_val_if_fail(file != NULL, -1);

	need_scan = rssyl_scan_required(folder, item);

	/* are we doing a folder move ? */
	tmp = g_strdup_printf("%s.tmp", file);
	if (is_file_exist(tmp)) {
		claws_unlink(tmp);
		g_free(tmp);
		g_free(file);
		return 0;
	}
	g_free(tmp);
	if( claws_unlink(file) < 0 ) {
		FILE_OP_ERROR(file, "unlink");
		g_free(file);
		return -1;
	}

	if( !need_scan )
		item->mtime = time(NULL);

	g_free(file);
	return 0;
}

static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
{
	if (folder->klass != rssyl_folder_get_class())
		return FALSE;
	
	if( rssyl_subscribe_new_feed(
				FOLDER_ITEM(folder->node->data), uri, FALSE) != NULL )
		return TRUE;
	return FALSE;
}

/************************************************************************/

FolderClass *rssyl_folder_get_class()
{
	if( rssyl_class.idstr == NULL ) {
		rssyl_class.type = F_UNKNOWN;
		rssyl_class.idstr = "rssyl";
		rssyl_class.uistr = "RSSyl";

		/* Folder functions */
		rssyl_class.new_folder = rssyl_new_folder;
		rssyl_class.destroy_folder = rssyl_destroy_folder;
		rssyl_class.set_xml = folder_set_xml;
		rssyl_class.get_xml = folder_get_xml;
		rssyl_class.scan_tree = rssyl_scan_tree;
		rssyl_class.create_tree = rssyl_create_tree;

		/* FolderItem functions */
		rssyl_class.item_new = rssyl_item_new;
		rssyl_class.item_destroy = rssyl_item_destroy;
		rssyl_class.item_get_path = rssyl_item_get_path;
		rssyl_class.create_folder = rssyl_create_folder;
		rssyl_class.rename_folder = rssyl_rename_folder;
		rssyl_class.remove_folder = rssyl_remove_folder;
		rssyl_class.get_num_list = rssyl_get_num_list;
		rssyl_class.scan_required = rssyl_scan_required;

		/* Message functions */
		rssyl_class.get_msginfo = rssyl_get_msginfo;
		rssyl_class.fetch_msg = rssyl_fetch_msg;
		rssyl_class.copy_msg = rssyl_dummy_copy_msg;
		rssyl_class.add_msg = rssyl_add_msg;
		rssyl_class.add_msgs = rssyl_add_msgs;
		rssyl_class.remove_msg = rssyl_remove_msg;
		rssyl_class.remove_msgs = NULL;
//		rssyl_class.change_flags = rssyl_change_flags;
		rssyl_class.change_flags = NULL;
		rssyl_class.subscribe = rssyl_subscribe_uri;
		debug_print("RSSyl: registered folderclass\n");
	}

	return &rssyl_class;
}

--- NEW FILE: rssyl_cb_menu.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - callback handler functions for folderview rssyl context menu items
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include <gtk/gtk.h>

#include "folderview.h"
#include "alertpanel.h"
#include "gtk/inputdialog.h"
#include "prefs_common.h"
#include "filesel.h"
#include "inc.h"
#include "folder_item_prefs.h"

#include "feed.h"
#include "feedprops.h"
#include "opml.h"
#include "rssyl.h"
#include "rssyl_gtk.h"

void rssyl_new_feed_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
	FolderItem *item;
	gchar *new_feed;

	debug_print("RSSyl: new_feed_cb\n");

	g_return_if_fail(folderview->selected != NULL);

	item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);

	new_feed = input_dialog(_("Subscribe feed"),
					 _("Input the URL of the news feed you wish to subscribe:"),
					 "");
	g_return_if_fail(new_feed != NULL);

	rssyl_subscribe_new_feed(item, new_feed, TRUE);

	g_free(new_feed);
}

void rssyl_new_folder_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
	FolderItem *item;
	FolderItem *new_item;
	gchar *new_folder;
	gchar *name;
	gchar *p;
	RSSylFolderItem *ritem = NULL;

	if (!folderview->selected) return;

	item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);

	new_folder = input_dialog(_("New folder"),
				  _("Input the name of new folder:"),
				  _("NewFolder"));
	if (!new_folder) return;
	AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});

	p = strchr(new_folder, G_DIR_SEPARATOR);
	if (p) {
		alertpanel_error(_("'%c' can't be included in folder name."),
				 G_DIR_SEPARATOR);
		return;
	}

	name = trim_string(new_folder, 32);
	AUTORELEASE_STR(name, {g_free(name); return;});

	/* find whether the directory already exists */
	if (folder_find_child_item_by_name(item, new_folder)) {
		alertpanel_error(_("The folder '%s' already exists."), name);
		return;
	}

	new_item = folder_create_folder(item, new_folder);
	if (!new_item) {
		alertpanel_error(_("Can't create the folder '%s'."), name);
		return;
	}

	ritem = (RSSylFolderItem *)new_item;
	ritem->url = NULL;

	folder_write_list();
}

void rssyl_remove_rss_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	FolderItem *item;
	gchar *name, *message;
	AlertValue aval;

	debug_print("RSSyl: remove_rss_cb\n");

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);
	g_return_if_fail( !folder_item_parent(item) );

	name = trim_string(item->folder->name, 32);
	message = g_strdup_printf(_("Really remove the folder tree '%s' ?\n"), name);
	aval = alertpanel_full(_("Remove folder tree"), message,
			 GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE, NULL,
			  ALERT_WARNING, G_ALERTDEFAULT);
	g_free(message);
	g_free(name);

	if (aval != G_ALERTALTERNATE)
		return;

	folderview_unselect(folderview);
	summary_clear_all(folderview->summaryview);

	folder_destroy(item->folder);
}

void rssyl_remove_feed_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
	FolderItem *item;
	gint response;
	GtkWidget *dialog, *rmcache_widget = NULL;
	gboolean rmcache = FALSE;
	debug_print("RSSyl: remove_feed_cb\n");

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	dialog = rssyl_feed_removal_dialog(item->name, &rmcache_widget);

	g_return_if_fail(dialog != NULL);

	gtk_widget_show_all(dialog);

	response = gtk_dialog_run(GTK_DIALOG(dialog));

	if( response != GTK_RESPONSE_YES ) {
		debug_print("'No' clicked, returning\n");
		gtk_widget_destroy(dialog);
		return;
	}

	g_return_if_fail(rmcache_widget != NULL);

	rmcache = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rmcache_widget) );

	gtk_widget_destroy(dialog);

	if( folderview->opened == folderview->selected ||
			gtk_cmctree_is_ancestor(ctree,
					folderview->selected,
					folderview->opened) ) {
		summary_clear_all(folderview->summaryview);
		folderview->opened = NULL;
	}

	rssyl_remove_feed_props((RSSylFolderItem *)item);

	if( rmcache == TRUE )
		rssyl_remove_feed_cache(item);

	if( item->folder->klass->remove_folder(item->folder, item) < 0 ) {
		gchar *tmp;
		tmp = g_markup_printf_escaped(_("Can't remove feed '%s'."), item->name);
		alertpanel_error("%s", tmp);
		g_free(tmp);
		if( folderview->opened == folderview->selected )
			summary_show(folderview->summaryview,
					folderview->summaryview->folder_item);
		return;
	}

	folder_write_list();
}

void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
	FolderItem *item;
	gchar *message, *name;
	AlertValue avalue;
	gchar *old_path;
	gchar *old_id;

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	name = trim_string(item->name, 32);
	AUTORELEASE_STR(name, {g_free(name); return;});
	message = g_strdup_printf
		(_("All folders and messages under '%s' will be permanently deleted. "
		   "Recovery will not be possible.\n\n"
		   "Do you really want to delete?"), name);
	avalue = alertpanel_full(_("Delete folder"), message,
				 GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, FALSE,
				 NULL, ALERT_WARNING, G_ALERTDEFAULT);
	g_free(message);
	if (avalue != G_ALERTALTERNATE) return;

	Xstrdup_a(old_path, item->path, return);
	old_id = folder_item_get_identifier(item);

	if (folderview->opened == folderview->selected ||
	    gtk_cmctree_is_ancestor(ctree,
				  folderview->selected,
				  folderview->opened)) {
		summary_clear_all(folderview->summaryview);
		folderview->opened = NULL;
	}

	if (item->folder->klass->remove_folder(item->folder, item) < 0) {
		folder_item_scan(item);
		alertpanel_error(_("Can't remove the folder '%s'."), name);
		g_free(old_id);
		return;
	}

	folder_write_list();

	prefs_filtering_delete_path(old_id);
	g_free(old_id);

}

void rssyl_refresh_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	FolderItem *item;
	RSSylFolderItem *ritem;

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);

	ritem = (RSSylFolderItem *)item;

	if (prefs_common_get_prefs()->work_offline && 
	   !inc_offline_should_override(TRUE,
			ngettext(
			   "Claws Mail needs network access in order "
			   "to update the feed.",
			   "Claws Mail needs network access in order "
			   "to update the feeds.", 1))) {
			return;
	}

	main_window_cursor_wait(mainwindow_get_mainwindow());
	rssyl_update_feed(ritem);
	main_window_cursor_normal(mainwindow_get_mainwindow());
}

void rssyl_refresh_all_cb(GtkAction *action, gpointer data)
{
	main_window_cursor_wait(mainwindow_get_mainwindow());
	rssyl_refresh_all_feeds();
	main_window_cursor_normal(mainwindow_get_mainwindow());
}

void rssyl_prop_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	FolderItem *item;
	RSSylFolderItem *ritem;

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);

	debug_print("RSSyl: rssyl_prop_cb() for '%s'\n", item->name);

	ritem = (RSSylFolderItem *)item;
	rssyl_get_feed_props(ritem);
	rssyl_gtk_prop(ritem);
}

void rssyl_rename_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	FolderItem *item;
	gchar *new_folder;
	gchar *name;
	gchar *message;

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->path != NULL);
	g_return_if_fail(item->folder != NULL);

	name = trim_string(item->name, 32);
	message = g_strdup_printf(_("Input new name for '%s':"), name);
	new_folder = input_dialog(_("Rename folder"), message, item->name);
	g_free(message);
	g_free(name);
	if (!new_folder) return;
	AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});

	if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
		alertpanel_error(_("'%c' can't be included in folder name."),
				 G_DIR_SEPARATOR);
		return;
	}

	if (folder_find_child_item_by_name(folder_item_parent(item), new_folder)) {
		name = trim_string(new_folder, 32);
		alertpanel_error(_("The folder '%s' already exists."), name);
		g_free(name);
		return;
	}

	if (folder_item_rename(item, new_folder) < 0) {
		alertpanel_error(_("The folder could not be renamed.\n"
				   "The new folder name is not allowed."));
		return;
	}

	folder_item_prefs_save_config_recursive(item);
	folder_write_list();
}

void rssyl_import_feed_list_cb(GtkAction *action, gpointer data)
{
	FolderView *folderview = (FolderView *)data;
	debug_print("RSSyl: rssyl_import_feed_cb\n");

	FolderItem *item;
	gchar *opmlfile = NULL;

	item = folderview_get_selected_item(folderview);
	g_return_if_fail(item != NULL);
	g_return_if_fail(item->folder != NULL);

	opmlfile = filesel_select_file_open_with_filter(
			_("Select a .opml file"), NULL, "*.opml");
	
	if (!is_file_exist(opmlfile)) {
		g_free(opmlfile);
		return;
	}

	rssyl_opml_import(opmlfile, item);
}

--- NEW FILE: Makefile.am ---
EXTRA_DIST = claws.def plugin.def version.rc

if OS_WIN32

LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RC) \
     `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
     sed -e 's/-I/--include-dir /g;s/-D/--define /g'`

%.lo : %.rc
	$(LTRCCOMPILE) -i $< -o $@

plugin_res = version.lo
plugin_res_ldflag = -Wl,.libs/version.o

export_symbols = -export-symbols $(srcdir)/plugin.def

plugin_deps = libclaws.a $(plugin_res) plugin.def

libclaws.a: claws.def
	$(DLLTOOL) --output-lib $@ --def $<

plugin_ldadd = -L. -lclaws

else
plugin_res =
plugin_res_ldflag =
export_symbols =
plugin_deps =
plugin_ldadd =
endif

if PLATFORM_WIN32
no_undefined = -no-undefined
else
no_undefined =
endif

if CYGWIN
cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
else
cygwin_export_lib = 
endif

plugindir = $(pkglibdir)/plugins

plugin_LTLIBRARIES = rssyl.la

INCLUDES= \
	-I$(top_srcdir)/src \
	-I$(top_srcdir)/src/common \
	-I$(top_builddir)/src/common \
	-I$(top_srcdir)/src/gtk

rssyl_la_LDFLAGS = \
	$(plugin_res_ldflag) $(no_undefined) $(export_symbols) \
	-avoid-version -module \
	$(GTK_LIBS)

rssyl_la_DEPENDENCIES = $(plugin_deps)

rssyl_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
	$(GTK_LIBS) $(LIBXML_LIBS) $(CURL_LIBS)

AM_CPPFLAGS = \
	-Wall \
	$(CLAWS_MAIL_CFLAGS) \
	$(GLIB_CFLAGS) \
	$(GTK_CFLAGS) \
	$(LIBXML_CFLAGS) \
	$(CURL_CFLAGS) \
	-DLOCALEDIR=\""$(localedir)"\" 

rssyl_la_SOURCES = \
	date.c date.h \
	feed.c feed.h \
	feedprops.c feedprops.h \
	opml.c opml.h \
	parsers.c parsers.h \
	plugin.c \
	rssyl.c rssyl.h \
	rssyl_cb_gtk.c rssyl_cb_gtk.h \
	rssyl_cb_menu.c rssyl_cb_menu.h \
	rssyl_gtk.c rssyl_gtk.h \
	rssyl_prefs.c rssyl_prefs.h \
	strreplace.c strreplace.h


--- NEW FILE: feedprops.h ---
#ifndef __FEEDPROPS_H
#define __FEEDPROPS_H

#include "rssyl.h"

#define RSSYL_PROPS_FILE	"feeds.xml"
#define RSSYL_PROPS_XPATH	"/feeds/feed"

#define RSSYL_PROP_URL					"url"
#define RSSYL_PROP_NAME					"name"
#define RSSYL_PROP_OFFICIAL_NAME			"official_name"
#define RSSYL_PROP_DEF_REFRESH	"default_refresh_interval"
#define RSSYL_PROP_REFRESH			"refresh_interval"
#define RSSYL_PROP_DEF_EXPIRED	"default_expired_num"
#define RSSYL_PROP_EXPIRED			"expired_num"
#define RSSYL_PROP_FETCH_COMMENTS		"fetch_comments"
#define RSSYL_PROP_FETCH_COMMENTS_FOR		"fetch_comments_for"
#define RSSYL_PROP_SILENT_UPDATE "silent_update"

void rssyl_store_feed_props(RSSylFolderItem *ritem);
void rssyl_get_feed_props(RSSylFolderItem *ritem);
void rssyl_remove_feed_props(RSSylFolderItem *ritem);
void rssyl_props_update_name(RSSylFolderItem *ritem, gchar *new_name);

#endif /* __FEEDPROPS_H */

--- NEW FILE: feedprops.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - functions for handling feeds.xml file
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <glib.h>

#include <libxml/parser.h>
#include <libxml/xpath.h>

#include "folder.h"

#include "feed.h"
#include "feedprops.h"
#include "rssyl.h"
#include "rssyl_prefs.h"

static gchar *rssyl_get_props_path(void)
{
	return g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
			G_DIR_SEPARATOR_S, RSSYL_PROPS_FILE, NULL);
}


/* rssyl_store_feed_props()
 *
 * Stores feed properties into feeds.xml file in the rcdir. Updates if already
 * stored for this feed.
 */
void rssyl_store_feed_props(RSSylFolderItem *ritem)
{
	gchar *path;
	xmlDocPtr doc;
	xmlNodePtr node, rootnode;
	xmlXPathObjectPtr result;
	xmlXPathContextPtr context;
	FolderItem *item = &ritem->item;
	gboolean found = FALSE, def_ri, def_ex;
	gint i;

	g_return_if_fail(ritem != NULL);
	g_return_if_fail(ritem->url != NULL);

	def_ri = ritem->default_refresh_interval;
	if( def_ri )
		ritem->refresh_interval = rssyl_prefs_get()->refresh;

	def_ex = ritem->default_expired_num;
	if( def_ex )
		ritem->expired_num = rssyl_prefs_get()->expired;

	path = rssyl_get_props_path();

	if( !(doc = xmlParseFile(path)) ) {
		debug_print("RSSyl: file %s doesn't exist, creating it\n", path);
		doc = xmlNewDoc("1.0");

		rootnode = xmlNewNode(NULL, "feeds");
		xmlDocSetRootElement(doc, rootnode);
	} else {
		rootnode = xmlDocGetRootElement(doc);
	}

	context = xmlXPathNewContext(doc);
	if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
		debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
		xmlXPathFreeContext(context);
	} else {
		for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
			gchar *tmp, *t_prop = NULL;
			node = result->nodesetval->nodeTab[i];
			tmp = xmlGetProp(node, RSSYL_PROP_NAME);

			if( !strcmp(tmp, item->name) ) {
				debug_print("RSSyl: XML - updating node for '%s'\n", item->name);

				xmlSetProp(node, RSSYL_PROP_NAME, item->name);
				xmlSetProp(node, RSSYL_PROP_OFFICIAL_NAME, 
						ritem->official_name?ritem->official_name:item->name);
				xmlSetProp(node, RSSYL_PROP_URL, ritem->url);

				xmlSetProp(node, RSSYL_PROP_DEF_REFRESH, (def_ri ? "1" : "0") );
				if( !def_ri ) {
					t_prop = g_strdup_printf("%d", ritem->refresh_interval);
					xmlSetProp(node, RSSYL_PROP_REFRESH,
							t_prop );
					g_free(t_prop);
				}

				xmlSetProp(node, RSSYL_PROP_DEF_EXPIRED, (def_ex ? "1" : "0") );
				if( !def_ex ) {
					t_prop = g_strdup_printf("%d", ritem->expired_num);
					xmlSetProp(node, RSSYL_PROP_EXPIRED,
							t_prop );
					g_free(t_prop);
				}
				xmlSetProp(node, RSSYL_PROP_FETCH_COMMENTS,
						(ritem->fetch_comments ? "1" : "0"));
				t_prop = g_strdup_printf("%d", ritem->fetch_comments_for);
				xmlSetProp(node, RSSYL_PROP_FETCH_COMMENTS_FOR, t_prop);
				g_free(t_prop);

				t_prop = g_strdup_printf("%d", ritem->silent_update);
				xmlSetProp(node, RSSYL_PROP_SILENT_UPDATE, t_prop);
				g_free(t_prop);

				found = TRUE;
			}
			xmlFree(tmp);
		}
	}

	xmlXPathFreeContext(context);
	xmlXPathFreeObject(result);

	/* node for our feed doesn't exist, let's make one */
	if( !found ) {
		debug_print("RSSyl: XML - creating node for '%s', storing URL '%s'\n",
				item->name, ritem->url);
		node = xmlNewTextChild(rootnode, NULL, "feed", NULL);
		xmlSetProp(node, RSSYL_PROP_NAME, item->name);
		xmlSetProp(node, RSSYL_PROP_OFFICIAL_NAME, 
				ritem->official_name?ritem->official_name:item->name);
		xmlSetProp(node, RSSYL_PROP_URL, ritem->url);
		xmlSetProp(node, RSSYL_PROP_DEF_REFRESH, (def_ri ? "1" : "0") );
		if( !def_ri )
			xmlSetProp(node, RSSYL_PROP_REFRESH,
					g_strdup_printf("%d", ritem->refresh_interval) );
		xmlSetProp(node, RSSYL_PROP_DEF_EXPIRED, (def_ex ? "1" : "0") );
		if( !def_ex )
			xmlSetProp(node, RSSYL_PROP_EXPIRED,
					g_strdup_printf("%d", ritem->expired_num) );
	}

	xmlSaveFormatFile(path, doc, 1);
	xmlFreeDoc(doc);
	g_free(path);
}

/* rssyl_get_feed_props()
 *
 * Retrieves props for feed from feeds.xml file in the rcdir.
 */
void rssyl_get_feed_props(RSSylFolderItem *ritem)
{
	gchar *path, *tmp = NULL;
	xmlDocPtr doc;
	xmlNodePtr node;
	xmlXPathObjectPtr result;
	xmlXPathContextPtr context;
	FolderItem *item = &ritem->item;
	gint i, tmpi;
	gboolean force_update = FALSE;
	RSSylPrefs *rsprefs = NULL;

	g_return_if_fail(ritem != NULL);

	if( ritem->url ) {
		g_free(ritem->url);
		ritem->url = NULL;
	}

	ritem->default_refresh_interval = TRUE;
	ritem->refresh_interval = rssyl_prefs_get()->refresh;
	ritem->expired_num = rssyl_prefs_get()->expired;

	path = rssyl_get_props_path();

	doc = xmlParseFile(path);
	g_return_if_fail(doc != NULL);

	context = xmlXPathNewContext(doc);
	if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
		debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
		xmlXPathFreeContext(context);
	} else {
		for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
			gchar *property = NULL;
			node = result->nodesetval->nodeTab[i];
			property = xmlGetProp(node, RSSYL_PROP_NAME);
			if( !strcmp(property, item->name) ) {
				/* official name */
				tmp = xmlGetProp(node, RSSYL_PROP_OFFICIAL_NAME);
				ritem->official_name = (tmp ? g_strdup(tmp) : g_strdup(item->name));
				if (tmp == NULL)
					force_update = TRUE;
				xmlFree(tmp);
				tmp = NULL;

				/* URL */
				tmp = xmlGetProp(node, RSSYL_PROP_URL);
				ritem->url = (tmp ? g_strdup(tmp) : NULL);
				xmlFree(tmp);
				tmp = NULL;

				tmp = xmlGetProp(node, RSSYL_PROP_DEF_REFRESH);
				tmpi = 0;
				if( tmp )
					tmpi = atoi(tmp);
				ritem->default_refresh_interval = (tmpi ? TRUE : FALSE );
				xmlFree(tmp);
				tmp = NULL;

				/* refresh_interval */
				tmp = xmlGetProp(node, RSSYL_PROP_REFRESH);
				if( ritem->default_refresh_interval )
					ritem->refresh_interval = rssyl_prefs_get()->refresh;
				else {
					tmpi = -1;
					if( tmp )
						tmpi = atoi(tmp);

					ritem->refresh_interval =
						(tmpi != -1 ? tmpi : rssyl_prefs_get()->refresh);
				}
				
				xmlFree(tmp);
				tmp = NULL;

				/* expired_num */
				tmp = xmlGetProp(node, RSSYL_PROP_DEF_EXPIRED);
				tmpi = 0;
				if( tmp ) {
					tmpi = atoi(tmp);
					ritem->default_expired_num = tmpi;
				}
				xmlFree(tmp);
				tmp = NULL;

				/* fetch_comments */
				tmp = xmlGetProp(node, RSSYL_PROP_FETCH_COMMENTS);
				tmpi = 0;
				if( tmp ) {
					tmpi = atoi(tmp);
					ritem->fetch_comments = tmpi;
				}
				xmlFree(tmp);
				tmp = NULL;

				/* fetch_comments_for */
				tmp = xmlGetProp(node, RSSYL_PROP_FETCH_COMMENTS_FOR);
				tmpi = 0;
				if( tmp ) {
					tmpi = atoi(tmp);
					ritem->fetch_comments_for = tmpi;
				}
				xmlFree(tmp);
				tmp = NULL;

				/* silent_update */
				tmp = xmlGetProp(node, RSSYL_PROP_SILENT_UPDATE);
				tmpi = 0;
				if( tmp ) {
					tmpi = atoi(tmp);
					ritem->silent_update = tmpi;
				}
				xmlFree(tmp);
				tmp = NULL;

				tmp = xmlGetProp(node, RSSYL_PROP_EXPIRED);

				if( ritem->default_expired_num )
					ritem->expired_num = rssyl_prefs_get()->expired;
				else {
					tmpi = -2;
					if( tmp )
						tmpi = atoi(tmp);

					ritem->expired_num = (tmpi != -2 ? tmpi : rssyl_prefs_get()->expired);
				}

				xmlFree(tmp);
				tmp = NULL;

				debug_print("RSSyl: XML - props for '%s' loaded\n", item->name);

				/* Start automatic refresh timer, if necessary */
				if( ritem->refresh_id == 0 ) {
					/* Check if user wants the default for this feed */
					if( ritem->default_refresh_interval ) {
						rsprefs = rssyl_prefs_get();
						ritem->refresh_interval = rsprefs->refresh;
					}

					/* Start the timer, if determined interval is >0 */
					if( ritem->refresh_interval >= 0 )
						rssyl_start_refresh_timeout(ritem);
				}
			}
			xmlFree(property);
		}
	}

	xmlXPathFreeObject(result);
	xmlXPathFreeContext(context);
	xmlFreeDoc(doc);
	g_free(path);
	if (force_update)
		rssyl_store_feed_props(ritem);
}

void rssyl_remove_feed_props(RSSylFolderItem *ritem)
{
	gchar *path;
	xmlDocPtr doc;
	xmlNodePtr node;
	xmlXPathObjectPtr result;
	xmlXPathContextPtr context;
	FolderItem *item = &ritem->item;
	gint i;

	g_return_if_fail(ritem != NULL);

	path = rssyl_get_props_path();

	doc = xmlParseFile(path);
	g_return_if_fail(doc != NULL);

	context = xmlXPathNewContext(doc);
	if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
		debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
		xmlXPathFreeContext(context);
	} else {
		for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
			gchar *tmp;
			node = result->nodesetval->nodeTab[i];
			tmp = xmlGetProp(node, RSSYL_PROP_NAME);
			if( !strcmp(tmp, item->name) ) {
				debug_print("RSSyl: XML - found node for '%s', removing\n", item->name);
				xmlUnlinkNode(node);
			}
			xmlFree(tmp);
		}
	}

	xmlXPathFreeObject(result);
	xmlXPathFreeContext(context);

	xmlSaveFormatFile(path, doc, 1);

	xmlFreeDoc(doc);
	g_free(path);
}

void rssyl_props_update_name(RSSylFolderItem *ritem, gchar *new_name)
{
	gchar *path;
	xmlDocPtr doc;
	xmlNodePtr node, rootnode;
	xmlXPathObjectPtr result;
	xmlXPathContextPtr context;
	FolderItem *item = &ritem->item;
	gboolean found = FALSE;
	gint i;

	g_return_if_fail(ritem != NULL);
	g_return_if_fail(ritem->url != NULL);

	path = rssyl_get_props_path();

	if( !(doc = xmlParseFile(path)) ) {
		debug_print("RSSyl: file %s doesn't exist, creating it\n", path);
		doc = xmlNewDoc("1.0");

		rootnode = xmlNewNode(NULL, "feeds");
		xmlDocSetRootElement(doc, rootnode);
	} else {
		rootnode = xmlDocGetRootElement(doc);
	}

	context = xmlXPathNewContext(doc);
	if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
		debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
		xmlXPathFreeContext(context);
	} else {
		for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
			gchar *tmp;
			node = result->nodesetval->nodeTab[i];
			tmp = xmlGetProp(node, RSSYL_PROP_NAME);
			if( !strcmp(tmp, item->name) ) {
				debug_print("RSSyl: XML - updating node for '%s'\n", item->name);
				xmlSetProp(node, "name", new_name);
				found = TRUE;
			}
			xmlFree(tmp);
		}
	}

	xmlXPathFreeContext(context);
	xmlXPathFreeObject(result);

	if( !found )
		debug_print("couldn't find feed\n");

	xmlSaveFormatFile(path, doc, 1);
	xmlFreeDoc(doc);
	g_free(path);
}

--- NEW FILE: rssyl_prefs.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - Plugin preferences
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include "common/defs.h"
#include "common/utils.h"
#include "gtk/gtkutils.h"
#include "prefs_gtk.h"

#include "rssyl_prefs.h"

static RSSylPrefsPage rssyl_prefs_page;
RSSylPrefs rssyl_prefs;

static void destroy_rssyl_prefs_page(PrefsPage *page);
static void create_rssyl_prefs_page(PrefsPage *page,
		GtkWindow *window, gpointer data);
static void save_rssyl_prefs(PrefsPage *page);

static PrefParam param[] = {
	{ "refresh_interval", RSSYL_PREF_DEFAULT_REFRESH, &rssyl_prefs.refresh, P_INT,
		NULL, NULL, NULL },
	{ "expired_keep", RSSYL_PREF_DEFAULT_EXPIRED, &rssyl_prefs.expired, P_INT,
		NULL, NULL, NULL },
	{ "refresh_on_startup", FALSE, &rssyl_prefs.refresh_on_startup, P_BOOL,
		NULL, NULL, NULL },
	{ "cookies_path", "", &rssyl_prefs.cookies_path, P_STRING,
		NULL, NULL, NULL },
	{ 0, 0, 0, 0, 0, 0, 0 }
};

void rssyl_prefs_init(void)
{
	static gchar *path[3];
	gchar *rcpath;

	path[0] = _("Plugins");
	path[1] = "RSSyl";		/* We don't need this translated */
	path[2] = NULL;

	prefs_set_default(param);
	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
	prefs_read_config(param, PREFS_BLOCK_NAME, rcpath, NULL);
	g_free(rcpath);

	rssyl_prefs_page.page.path = path;
	rssyl_prefs_page.page.create_widget = create_rssyl_prefs_page;
	rssyl_prefs_page.page.destroy_widget = destroy_rssyl_prefs_page;
	rssyl_prefs_page.page.save_page = save_rssyl_prefs;
	rssyl_prefs_page.page.weight = 30.0;

	prefs_gtk_register_page((PrefsPage *) &rssyl_prefs_page);
}

void rssyl_prefs_done(void)
{
	prefs_gtk_unregister_page((PrefsPage *) &rssyl_prefs_page);
}

static void create_rssyl_prefs_page(PrefsPage *page,
		GtkWindow *window, gpointer data)
{
	RSSylPrefsPage *prefs_page = (RSSylPrefsPage *) page;
	GtkWidget *table;
	GtkWidget *refresh;
	GtkWidget *expired;
	GtkWidget *refresh_on_startup;
	GtkWidget *cookies_path;
	GtkWidget *label;
	GtkObject *refresh_adj, *expired_adj;

	table = gtk_table_new(RSSYL_NUM_PREFS, 2, FALSE);
	gtk_container_set_border_width(GTK_CONTAINER(table), 5);
	gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
	gtk_table_set_col_spacings(GTK_TABLE(table), 8);

	label = gtk_label_new(_("Default refresh interval in minutes"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
			GTK_FILL, 0, 0, 0);

	refresh_adj = gtk_adjustment_new(rssyl_prefs.refresh,
			0, 100000, 1, 10, 0);
	refresh = gtk_spin_button_new(GTK_ADJUSTMENT(refresh_adj), 1, 0);
	gtk_table_attach(GTK_TABLE(table), refresh, 1, 2, 0, 1,
			GTK_FILL, 0, 0, 0);
	CLAWS_SET_TIP(refresh,
			_("Set to 0 to disable automatic refreshing"));

	label = gtk_label_new(_("Default number of expired items to keep"));
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
			GTK_FILL, 0, 0, 0);

	expired_adj = gtk_adjustment_new(rssyl_prefs.expired,
			-1, 100000, 1, 10, 0);
	expired = gtk_spin_button_new(GTK_ADJUSTMENT(expired_adj), 1, 0);
	gtk_table_attach(GTK_TABLE(table), expired, 1, 2, 1, 2,
			GTK_FILL, 0, 0, 0);
	CLAWS_SET_TIP(expired,
			_("Set to -1 to keep expired items"));

	refresh_on_startup = gtk_check_button_new_with_label(
			_("Refresh all feeds on application start"));
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(refresh_on_startup),
			rssyl_prefs.refresh_on_startup);
	gtk_table_attach(GTK_TABLE(table), refresh_on_startup, 0, 2, 3, 4,
			GTK_FILL | GTK_EXPAND, 0, 0, 0);

	label = gtk_label_new(_("Path to cookies file"));
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
			GTK_FILL, 0, 0, 0);
	gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);

	cookies_path = gtk_entry_new ();
	gtk_entry_set_text(GTK_ENTRY(cookies_path), rssyl_prefs.cookies_path);
	gtk_table_attach(GTK_TABLE(table), cookies_path, 1, 2, 4, 5,
			GTK_FILL | GTK_EXPAND, 0, 0, 0);
	CLAWS_SET_TIP(cookies_path,
			_("Path to Netscape-style cookies.txt file containing your cookies"));

	gtk_widget_show_all(table);

	prefs_page->page.widget = table;
	prefs_page->refresh = refresh;
	prefs_page->expired = expired;
	prefs_page->refresh_on_startup = refresh_on_startup;
	prefs_page->cookies_path = cookies_path;
}

static void destroy_rssyl_prefs_page(PrefsPage *page)
{
	/* Do nothing! */
}

static void save_rssyl_prefs(PrefsPage *page)
{
	RSSylPrefsPage *prefs_page = (RSSylPrefsPage *)page;
	PrefFile *pref_file;
	gchar *rc_file_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
			COMMON_RC, NULL);

	rssyl_prefs.refresh = gtk_spin_button_get_value_as_int(
			GTK_SPIN_BUTTON(prefs_page->refresh));
	rssyl_prefs.expired = gtk_spin_button_get_value_as_int(
			GTK_SPIN_BUTTON(prefs_page->expired));
	rssyl_prefs.refresh_on_startup = gtk_toggle_button_get_active(
			GTK_TOGGLE_BUTTON(prefs_page->refresh_on_startup));
	g_free(rssyl_prefs.cookies_path);
	rssyl_prefs.cookies_path = g_strdup(gtk_entry_get_text(
			GTK_ENTRY(prefs_page->cookies_path)));

	pref_file = prefs_write_open(rc_file_path);
	g_free(rc_file_path);

	if( !pref_file || prefs_set_block_label(pref_file, PREFS_BLOCK_NAME) < 0 )
				return;

	if( prefs_write_param(param, pref_file->fp) < 0 ) {
		g_warning("Failed to write RSSyl plugin configuration\n");
		prefs_file_close_revert(pref_file);
		return;
	}

        if (fprintf(pref_file->fp, "\n") < 0) {
		FILE_OP_ERROR(rc_file_path, "fprintf");
		prefs_file_close_revert(pref_file);
	} else
	        prefs_file_close(pref_file);
}

RSSylPrefs *rssyl_prefs_get(void)
{
	return &rssyl_prefs;
}

--- NEW FILE: date.c ---
/**
 * @file common.c common routines for Liferea
 * 
 * Copyright (C) 2003-2005  Lars Lindner <lars.lindner at gmx.net>
 * Copyright (C) 2004,2005  Nathan J. Conrad <t98502 at users.sourceforge.net>
 * Copyright (C) 2004       Karl Soderstrom <ks at xanadunet.net>
 *
 * parts of the RFC822 timezone decoding were taken from the gmime 
 * source written by 
 *
 * Authors: Michael Zucchi <notzed at helixcode.com>
 *          Jeffrey Stedfast <fejj at helixcode.com>
 *
 * Copyright 2000 Helix Code, Inc. (www.helixcode.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/* this is needed for strptime() */
#if !defined (__FreeBSD__)
#define _XOPEN_SOURCE 600 /* glibc2 needs this */
#else
#define _XOPEN_SOURCE
#endif

#ifdef USE_PTHREAD
#include <pthread.h>
#endif

#include <time.h>
#include <glib.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

#include "procheader.h"

/* converts a ISO 8601 time string to a time_t value */
time_t parseISO8601Date(gchar *date) {
	struct tm	tm;
	time_t		t, t2, offset = 0;
	gboolean	success = FALSE;
#ifdef G_OS_WIN32
	gchar *tmp = g_strdup(date);
	gint result, year, month, day, hour, minute, second;
#else
	gchar *pos;
#endif	
	g_assert(date != NULL);
	
	memset(&tm, 0, sizeof(struct tm));
	
#ifdef G_OS_WIN32
	g_strstrip(tmp);
	result = sscanf((const char *)date, "%d-%d-%dT%d:%d:%d", 
			&year, &month, &day, &hour, &minute, &second);
	if (result < 6)
		second = 0;
	if (result < 5)
		minute = 0;
	if (result < 4)
		hour = 0;
	if (result >= 3) {
		tm.tm_sec = second;
		tm.tm_min = minute;
		tm.tm_hour = hour;
		tm.tm_mday = day;
		tm.tm_mon = month - 1;
		tm.tm_year = year - 1900;
		tm.tm_wday = 0;
		tm.tm_yday = 0;
		tm.tm_isdst = -1;
		success = TRUE;
	}
#else
	/* we expect at least something like "2003-08-07T15:28:19" and
	   don't require the second fractions and the timezone info

	   the most specific format:   YYYY-MM-DDThh:mm:ss.sTZD
	 */
	 
	/* full specified variant */
	if(NULL != (pos = strptime((const char *)date, "%t%Y-%m-%dT%H:%M%t", &tm))) {
		/* Parse seconds */
		if (*pos == ':')
			pos++;
		if (isdigit(pos[0]) && !isdigit(pos[1])) {
			tm.tm_sec = pos[0] - '0';
			pos++;
		} else if (isdigit(pos[0]) && isdigit(pos[1])) {
			tm.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
			pos +=2;
		}
		/* Parse timezone */
		if (*pos == 'Z')
			offset = 0;
		else if ((*pos == '+' || *pos == '-') && isdigit(pos[1]) && isdigit(pos[2]) && strlen(pos) >= 3) {
			offset = (10*(pos[1] - '0') + (pos[2] - '0')) * 60 * 60;
			
			if (pos[3] == ':' && isdigit(pos[4]) && isdigit(pos[5]))
				offset +=  (10*(pos[4] - '0') + (pos[5] - '0')) * 60;
			else if (isdigit(pos[3]) && isdigit(pos[4]))
				offset +=  (10*(pos[3] - '0') + (pos[4] - '0')) * 60;
			
			offset *= (pos[0] == '+') ? 1 : -1;

		}
		success = TRUE;
	/* only date */
	} else if(NULL != strptime((const char *)date, "%t%Y-%m-%d", &tm))
		success = TRUE;
	/* there were others combinations too... */
#endif
	if(TRUE == success) {
		if((time_t)(-1) != (t = mktime(&tm))) {
			struct tm buft;
			/* Correct for the local timezone*/
			t = t - offset;
			t2 = mktime(gmtime_r(&t, &buft));
			t = t - (t2 - t);
			
			return t;
		} else {
			g_warning("internal error! time conversion error! mktime failed!\n");
		}
	} else {
		g_warning("Invalid ISO8601 date format! Ignoring <dc:date> information!\n");
	}
	
	return 0;
}

gchar *dayofweek[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
gchar *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

gchar *createRFC822Date(const time_t *time) {
	struct tm *tm;
	struct tm buft;
#ifdef G_OS_WIN32
	if (*time < 0) {
		time_t t = 1;
		tm = gmtime_r(&t, &buft);
	} else 
#endif
	{
		tm = gmtime_r(time, &buft); /* No need to free because it is statically allocated */
	}
	return g_strdup_printf("%s, %2d %s %4d %02d:%02d:%02d GMT", dayofweek[tm->tm_wday], tm->tm_mday,
					   months[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
}

--- NEW FILE: version.rc ---
1 VERSIONINFO
 FILEVERSION 0, 0, 0, 0
 PRODUCTVERSION 0, 0, 0, 0
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "000004b0"
        BEGIN
            VALUE "FileDescription", "RSSyl Plugin\0"
            VALUE "FileVersion", "0.0.0.0\0"
            VALUE "ProductVersion", "0.0.0.0 Win32\0"
            VALUE "LegalCopyright", "GPL / © 1999-2008 Hiroyuki Yamamoto & The Claws Mail Team\0"
            VALUE "CompanyName", "GNU / Free Software Foundation\0"
            VALUE "ProductName", "Claws Mail\0"
//            VALUE "Comments", "\0"
//            VALUE "InternalName", "\0"
//            VALUE "LegalTrademarks", "\0"
//            VALUE "OriginalFilename", "\0"
//            VALUE "PrivateBuild", "\0"
//            VALUE "SpecialBuild", "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x0, 1200
    END
END

--- NEW FILE: claws.def ---
LIBRARY CLAWS-MAIL.EXE
EXPORTS
get_locale_dir
check_plugin_version
alertpanel_error
alertpanel_full
change_dir
claws_do_idle
claws_is_exiting
claws_is_starting
claws_unlink
cm_menu_set_sensitive_full
conv_encode_header_full
conv_unmime_header
copy_file
debug_print_real
debug_srcname
dirent_is_regular_file
file_exist
filesel_select_file_open_with_filter
file_strip_crs
folder_add
folder_create_folder
folder_destroy
folder_find_child_item_by_name
folder_find_from_path
folder_func_to_all_folders
folder_get_class_from_string
folder_get_xml
folder_init
folder_item_add_msg
folder_item_append
folder_item_get_identifier
folder_item_get_msginfo
folder_item_get_path
folder_item_new
folder_item_parent
folder_item_prefs_save_config_recursive
folder_item_remove
folder_item_rename
folder_item_scan
folder_item_update_freeze
folder_item_update_thaw
folder_local_folder_destroy
folder_new
folder_register_class
folder_scan_tree
folder_set_xml
folder_unregister_class
folderview_get_selected_item
folderview_register_popup
folderview_set
folderview_unregister_popup
folderview_unselect
folder_write_list
get_rc_dir
get_tmp_file
gtk_cmctree_get_type
gtk_cmctree_is_ancestor
gtk_cmctree_node_get_row_data
gtkut_widget_draw_now
gtkut_window_new
gtkut_widget_set_can_default
inc_offline_should_override
input_dialog
is_dir_exist
is_file_entry_exist
log_error
log_print
log_warning
main_window_cursor_normal
main_window_cursor_wait
mainwindow_get_mainwindow
make_dir
make_dir_hier
mkstemp
prefs_common_get_prefs
prefs_file_close
prefs_file_close_revert
prefs_filtering_delete_path
prefs_gtk_register_page
prefs_gtk_unregister_page
prefs_read_config
prefs_set_block_label
prefs_set_default
prefs_toolbar_register_plugin_item
prefs_toolbar_unregister_plugin_item
prefs_write_open
prefs_write_param
procheader_date_parse
procheader_parse_file
procmsg_get_message_file
procmsg_msginfo_unset_flags
strtailchomp
subst_for_shellsafe_filename
summary_clear_all
summary_show
to_number
trim_string

--- NEW FILE: rssyl_prefs.h ---
#ifndef __RSSYL_PREFS
#define __RSSYL_PREFS

#define PREFS_BLOCK_NAME	"rssyl"

#define RSSYL_NUM_PREFS		4

#define RSSYL_PREF_DEFAULT_REFRESH	"180"
#define RSSYL_PREF_DEFAULT_EXPIRED	"-1"

typedef struct _RSSylPrefs RSSylPrefs;

struct _RSSylPrefs {
	gint refresh;
	gint expired;
	gboolean refresh_on_startup;
	gchar *cookies_path;
};

typedef struct _RSSylPrefsPage RSSylPrefsPage;

struct _RSSylPrefsPage {
	PrefsPage page;
	GtkWidget *refresh;
	GtkWidget *expired;
	GtkWidget *refresh_on_startup;
	GtkWidget *cookies_path;
};

void rssyl_prefs_init(void);
void rssyl_prefs_done(void);
RSSylPrefs *rssyl_prefs_get(void);

#endif /* __RSSYL_PREFS */

--- NEW FILE: plugin.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - generic s-c plugin stuff
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include "common/version.h"
#include "claws.h"
#include <curl/curl.h>

#include "rssyl.h"
#include "plugin.h"

gint plugin_init(gchar **error)
{
	if( !check_plugin_version(MAKE_NUMERIC_VERSION(3,7,8,31),
				VERSION_NUMERIC, PLUGIN_NAME, error) )
		return -1;

	curl_global_init(CURL_GLOBAL_DEFAULT);
	rssyl_init();

	return 0;
}

gboolean plugin_done(void)
{
	rssyl_done();
	return TRUE;
}

const gchar *plugin_name(void)
{
	return PLUGIN_NAME;
}

const gchar *plugin_desc(void)
{
	return _("This plugin allows you to create a mailbox tree where you can add "
		"newsfeeds in RSS 1.0, RSS 2.0 or Atom format.\n\n"
		"Each newsfeed will create a folder with appropriate entries, fetched "
		"from the web. You can read them, and delete or keep old entries.");
}

const gchar *plugin_type(void)
{
	return "GTK2";
}

const gchar *plugin_licence(void)
{
	return "GPL2+";
}

const gchar *plugin_version(void)
{
	return VERSION;
}

struct PluginFeature *plugin_provides(void)
{
	static struct PluginFeature features[] = 
		{ {PLUGIN_FOLDERCLASS, N_("RSS feed")},
		  {PLUGIN_NOTHING, NULL}};
	return features;
}

--- NEW FILE: parsers.h ---
#ifndef __PARSERS_H
#define __PARSERS_H

#include <glib.h>
#include <libxml/parser.h>

#include "feed.h"

gint rssyl_parse_rss(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent);
gint rssyl_parse_rdf(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent);
gint rssyl_parse_atom(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent);

#endif /* __PARSERS_H */

--- NEW FILE: parsers.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - various feed parsing functions
 * - this file could use some sorting and/or splitting
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <glib.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/HTMLtree.h>

#include "date.h"
#include "feed.h"
#include "strreplace.h"
#include "utils.h"
#include "procheader.h"

gint rssyl_parse_rdf(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
{
	xmlNodePtr rnode, node, n;
	RSSylFeedItem *fitem = NULL;
	gint count = 0;
	gchar *content = NULL;
	g_return_val_if_fail(doc != NULL, 0);
	g_return_val_if_fail(ritem != NULL, 0);
#ifdef RSSYL_DEBUG
	gchar *fetched = NULL;
#endif	/* RSSYL_DEBUG */

	if( ritem->contents == NULL )
		rssyl_read_existing(ritem);

	rnode = xmlDocGetRootElement(doc);

	for( node = rnode->children; node; node = node->next ) {
		if( !xmlStrcmp(node->name, "item") ) {
			/* We've found an "item" tag, let's poke through its contents */
			fitem = g_new0(RSSylFeedItem, 1);
			fitem->date = 0;
#ifdef RSSYL_DEBUG
			fetched = xmlGetProp(rnode, "fetched");
			fitem->debug_fetched = atoll(fetched);
			xmlFree(fetched);
#endif	/* RSSYL_DEBUG */

			for( n = node->children; n; n = n->next ) {
				/* Title */
				if( !xmlStrcmp(n->name, "title") ) {
					content = xmlNodeGetContent(n);
					fitem->title = rssyl_format_string(content, TRUE, TRUE);
					xmlFree(content);
					debug_print("RSSyl: XML - RDF title is '%s'\n", fitem->title);
				}

				/* Text */
				if( !xmlStrcmp(n->name, "description") ) {
					content = xmlNodeGetContent(n);
					fitem->text = rssyl_format_string(content, FALSE, FALSE);
					xmlFree(content);
					debug_print("RSSyl: XML - got RDF text\n");
				}

				/* URL */
				if( !xmlStrcmp(n->name, "link") ) {
					content = xmlNodeGetContent(n);
					fitem->link = rssyl_format_string(content, FALSE, TRUE);
					xmlFree(content);
					debug_print("RSSyl: XML - RDF link is '%s'\n", fitem->link);
				}

				/* Date - rfc822 format */
				if( !xmlStrcmp(n->name, "pubDate") ) {
					content = xmlNodeGetContent(n);
					fitem->date = procheader_date_parse(NULL, content, 0);
					xmlFree(content);
					if( fitem->date > 0 ) {
						debug_print("RSSyl: XML - RDF pubDate found\n" );
					} else
						fitem->date = 0;
				}
				/* Date - ISO8701 format */
				if( !xmlStrcmp(n->name, "date") &&
						(!xmlStrcmp(n->ns->prefix, "ns")
						 || !xmlStrcmp(n->ns->prefix, "dc")) ) {
					content = xmlNodeGetContent(n);
					fitem->date = parseISO8601Date(content);
					xmlFree(content);
					debug_print("RSSyl: XML - RDF date found\n" );
				}

				/* Author */
				if( !xmlStrcmp(n->name, "creator") ) {
					content = xmlNodeGetContent(n);
					fitem->author = rssyl_format_string(content, TRUE, TRUE);
					xmlFree(content);
					debug_print("RSSyl: XML - RDF author is '%s'\n", fitem->author);
				}
			}
		}

		if( fitem && fitem->link && fitem->title ) {
			if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
				rssyl_free_feeditem(fitem);
				fitem = NULL;
			}
			fitem = NULL;
			count++;
		}
	}

	return count;
}


/* rssyl_parse_rss()
 *
 * This is where we parse the fetched rss document and create a
 * RSSylFolderItem from it. Returns number of parsed items
 */
gint rssyl_parse_rss(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
{
	xmlXPathContextPtr context;
	xmlXPathObjectPtr result;
	xmlNodePtr node, n, rnode;
	gint i, count = 0;
	RSSylFeedItem *fitem = NULL;
	gchar *xpath;
	gboolean got_encoded, got_author;
	gchar *rootnode = NULL;
	RSSylFeedItemMedia *media;
	gchar *media_url, *media_type;
	gulong media_size = 0;
#ifdef RSSYL_DEBUG
	gchar *fetched = NULL;
#endif	/* RSSYL_DEBUG */

	g_return_val_if_fail(doc != NULL, 0);
	g_return_val_if_fail(ritem != NULL, 0);

	if( ritem->contents == NULL )
		rssyl_read_existing(ritem);

	rnode = xmlDocGetRootElement(doc);

	rootnode = g_ascii_strdown(rnode->name, -1);
	xpath = g_strconcat("/", rootnode,
				"/channel/item",	NULL);
	g_free(rootnode);
	context = xmlXPathNewContext(doc);
	if( !(result = xmlXPathEvalExpression(xpath, context)) ){
		debug_print("RSSyl: XML - no result found for '%s'\n", xpath);
		xmlXPathFreeContext(context);
		g_free(xpath);
		return 0;
	}

	g_free(xpath);

	for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
		node = result->nodesetval->nodeTab[i];
		
		if ((n = node->children) == NULL)
			continue;

		fitem = g_new0(RSSylFeedItem, 1);
		fitem->media = NULL;
		fitem->date = 0;
#ifdef RSSYL_DEBUG
		fetched = xmlGetProp(rnode, "fetched");
		fitem->debug_fetched = atoll(fetched);
		xmlFree(fetched);
#endif	/* RSSYL_DEBUG */
		fitem->text = NULL;
		
		if (parent)
			fitem->parent_link = g_strdup(parent);

		got_encoded = FALSE;
		got_author = FALSE;
		do {
			gchar *content = NULL;

			/* Title */
			if( !xmlStrcmp(n->name, "title") ) {
				content = xmlNodeGetContent(n);
				fitem->title = rssyl_format_string(content, TRUE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - item title: '%s'\n", fitem->title);
			}

			/* Text */
			if( !xmlStrcmp(n->name, "description") ) {
				if( (fitem->text == NULL) && (got_encoded == FALSE) ) {
					content = xmlNodeGetContent(n);
					debug_print("RSSyl: XML - item text (description) caught\n");
					fitem->text = rssyl_format_string(content, FALSE, FALSE);
					xmlFree(content);
				}
			}
			if( !xmlStrcmp(n->name, "encoded")
					&& !xmlStrcmp(n->ns->prefix, "content") ) {
				debug_print("RSSyl: XML - item text (content) caught\n");

				if (fitem->text != NULL)
					g_free(fitem->text); /* free "description" */
					
				content = xmlNodeGetContent(n);
				fitem->text = rssyl_format_string(content, FALSE, FALSE);
				xmlFree(content);
				got_encoded = TRUE;
			}

			/* URL link to the original post */
			if( !xmlStrcmp(n->name, "link") ) {
				content = xmlNodeGetContent(n);
				fitem->link = rssyl_format_string(content, FALSE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - item link: '%s'\n", fitem->link);
			}

			/* GUID - sometimes used as link */
			if( !xmlStrcmp(n->name, "guid") ) {
				gchar *tmp = xmlGetProp(n, "isPermaLink");
				content = xmlNodeGetContent(n);
				fitem->id_is_permalink = FALSE;
				if( !tmp || xmlStrcmp(tmp, "false") )	/* permalink? */
					fitem->id_is_permalink = TRUE;
				fitem->id = rssyl_format_string(content, FALSE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - item guid: '%s'\n", fitem->id);
				xmlFree(tmp);
			}

			/* Date - rfc822 format */
			if( !xmlStrcmp(n->name, "pubDate") ) {
				content = xmlNodeGetContent(n);
				fitem->date = procheader_date_parse(NULL, content, 0);
				xmlFree(content);
				if( fitem->date > 0 ) {
					debug_print("RSSyl: XML - item date found: %d\n", (gint)fitem->date);
				} else
					fitem->date = 0;
			}
			/* Date - ISO8701 format */
			if( !xmlStrcmp(n->name, "date") && !xmlStrcmp(n->ns->prefix, "dc") ) {
				content = xmlNodeGetContent(n);
				fitem->date = parseISO8601Date(content);
				xmlFree(content);
				debug_print("RSSyl: XML - item date found\n" );
			}

			/* Author */
			if( !xmlStrcmp(n->name, "author") ) {
				content = xmlNodeGetContent(n);
				fitem->author = rssyl_format_string(content, TRUE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - item author: '%s'\n", fitem->author);
				got_author = TRUE;
			}

			if( !xmlStrcmp(n->name, "creator")
					&& !xmlStrcmp(n->ns->prefix, "dc") && !got_author) {
				content = xmlNodeGetContent(n);
				fitem->author = rssyl_format_string(content, TRUE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - item author (creator): '%s'\n", fitem->author);
			}

			/* Media enclosure */
			if( !xmlStrcmp(n->name, "enclosure") ) {
				gchar *tmp = xmlGetProp(n, "length");
				media_url = xmlGetProp(n, "url");
				media_type = xmlGetProp(n, "type");
				media_size = (tmp ? atoi(tmp) : 0);
				xmlFree(tmp);

				if( media_url != NULL &&
						media_type != NULL &&
						media_size != 0 ) {
					debug_print("RSSyl: XML - enclosure: '%s' [%s] (%ld)\n",
							media_url, media_type, media_size);
					media = g_new(RSSylFeedItemMedia, 1);
					media->url = media_url;
					media->type = media_type;
					media->size = media_size;
					fitem->media = media;
				} else {
					debug_print("RSSyl: XML - enclosure found, but some data is missing\n");
					g_free(media_url);
					g_free(media_type);
				}
			}

			/* Comments */
			if( !xmlStrcmp(n->name, "commentRSS") || !xmlStrcmp(n->name, "commentRss") ) {
				content = xmlNodeGetContent(n);
				fitem->comments_link = rssyl_format_string(content, FALSE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - comments_link: '%s'\n", fitem->comments_link);
			}
		} while( (n = n->next) != NULL);

		if( (fitem->link || fitem->id) && fitem->title ) {
			if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
				rssyl_free_feeditem(fitem);
				fitem = NULL;
			}
			count++;
		}
	}

	xmlXPathFreeObject(result);
	xmlXPathFreeContext(context);

	return count;
}

/* rssyl_parse_atom()
 *
 * This is where we parse the fetched atom document and create a
 * RSSylFolderItem from it. Returns number of parsed items
 */
gint rssyl_parse_atom(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
{
	xmlNodePtr node, n, h;
	xmlBufferPtr buf = NULL;
	gint count = 0;
	RSSylFeedItem *fitem = NULL;
	RSSylFeedItemMedia *media = NULL;
	gchar *link_type, *link_href, *link_rel, *tmp, *content = NULL;
	gulong link_size;

	g_return_val_if_fail(doc != NULL, 0);
	g_return_val_if_fail(ritem != NULL, 0);

	if( ritem->contents == NULL )
		rssyl_read_existing(ritem);

	node = xmlDocGetRootElement(doc);

	if (node == NULL)
		return 0;

	node = node->children;

	for (; node; node = node->next) {
		gboolean got_content = FALSE;
		if (xmlStrcmp(node->name, "entry")) {
			continue;
		}
	
		n = node->children;
		fitem = g_new0(RSSylFeedItem, 1);
		fitem->date = 0;
		fitem->date_published = 0;
		fitem->text = NULL;
		
		if (parent)
			fitem->parent_link = g_strdup(parent);

		do {
			/* Title */
			if( !xmlStrcmp(n->name, "title") ) {
				content = xmlNodeGetContent(n);
				fitem->title = rssyl_format_string(content, TRUE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - Atom item title: '%s'\n", fitem->title);
			}

			/* ID */
			if( !xmlStrcmp(n->name, "id") ) {
				content = xmlNodeGetContent(n);
				fitem->id = g_strdup_printf("%s%s", (parent?"comment-":""), content);
				xmlFree(content);
				debug_print("RSSyl: XML - Atom id: '%s'\n", fitem->id);
			}

			/* Text */
			if( !xmlStrcmp(n->name, "summary") && !got_content ) {
				content = xmlNodeGetContent(n);
				debug_print("RSSyl: XML - Atom item text (summary) caught\n");
				fitem->text = rssyl_format_string(content, FALSE, FALSE);
				xmlFree(content);
			}

			if( !xmlStrcmp(n->name, "content") ) {
				gchar *tmp = xmlGetProp(n, "type");
				debug_print("RSSyl: XML - Atom item text (content) caught\n");
				if (fitem->text)
					g_free(fitem->text);
				if( !xmlStrcmp(tmp, "xhtml")) {
					for( h = n->children; h; h = h->next ) {
						if( !xmlStrcmp(h->name, "div") ) {
							buf = xmlBufferCreate();
							htmlNodeDump(buf, doc, h);
							content = g_strdup((gchar *)xmlBufferContent(buf));
							xmlBufferFree(buf);
						}
					}
				} else
					content = xmlNodeGetContent(n);
				xmlFree(tmp);
				fitem->text = rssyl_format_string(content, FALSE, FALSE);
				xmlFree(content);
				got_content = TRUE;
			}

			/* link */
			if( !xmlStrcmp(n->name, "link") ) {
				link_type = xmlGetProp(n, "type");
				link_rel = xmlGetProp(n, "rel");
				link_href = xmlGetProp(n, "href");
				tmp = xmlGetProp(n, "length");
				link_size = (tmp ? atoi(tmp) : 0);
				g_free(tmp);

				if( !link_rel || (link_rel && !xmlStrcmp(link_rel, "alternate")) ) {
					fitem->link = link_href;
					debug_print("RSSyl: XML - Atom item link: '%s'\n", fitem->link);
					xmlFree(link_type);
					xmlFree(link_rel);
				} else if( link_rel && !xmlStrcmp(link_rel, "enclosure") ) {
					debug_print("RSSyl: XML - Atom item enclosure: '%s' (%ld) [%s]\n",
							link_href, link_size, link_type);
					media = g_new(RSSylFeedItemMedia, 1);
					media->url = link_href;
					media->type = link_type;
					media->size = link_size;
					fitem->media = media;
					xmlFree(link_rel);
				} else {
					xmlFree(link_type);
					xmlFree(link_rel);
					xmlFree(link_href);
				}
			}

			/* Date published - ISO8701 format */
			if( !xmlStrcmp(n->name, "published") ) {
				content = xmlNodeGetContent(n);
				fitem->date_published = parseISO8601Date(content);
				xmlFree(content);
				debug_print("RSSyl: XML - Atom item 'issued' date found\n" );
			}

			/* Date modified - ISO8701 format */
			if( !xmlStrcmp(n->name, "updated") ) {
				content = xmlNodeGetContent(n);
				fitem->date = parseISO8601Date(content);
				xmlFree(content);
				debug_print("RSSyl: XML - Atom item 'updated' date found\n" );
			}

			/* Author */
			if( !xmlStrcmp(n->name, "author") ) {
				xmlNodePtr subnode;
				gchar *name = NULL, *mail = NULL;
				gchar *tmp;
				for (subnode = n->children; subnode; subnode = subnode->next) {
					content = xmlNodeGetContent(subnode);
					if (!xmlStrcmp(subnode->name, "name") && !name)
						name = g_strdup(content);
					if (!xmlStrcmp(subnode->name, "email") && !mail)
						mail = g_strdup(content);
					xmlFree(content);
				}
				tmp = g_strdup_printf("%s%s%s%s%s",
							name ? name:"",
							name && mail ? " <":(mail?"<":""),
							mail ? mail:"",
							mail ? ">":"",
							!name && !mail ? "N/A":"");
				fitem->author = rssyl_format_string(tmp, TRUE, TRUE);
				g_free(tmp);
				g_free(name);
				g_free(mail);
				debug_print("RSSyl: XML - Atom item author: '%s'\n", fitem->author);
			}

			/* Comments */
			if( !xmlStrcmp(n->name, "commentRSS") || !xmlStrcmp(n->name, "commentRss")) {
				content = xmlNodeGetContent(n);
				fitem->comments_link = rssyl_format_string(content, FALSE, TRUE);
				xmlFree(content);
				debug_print("RSSyl: XML - comments_link: '%s'\n", fitem->comments_link);
			}
		} while( (n = n->next) != NULL);

		if( fitem->id && fitem->title && fitem->date ) {

			/* If no link is available, and we can safely guess ID
			 * might be a (perma)link, mark it so. */
			if (!fitem->link && fitem->id	/* no url, but we have id */
					&& (!strncmp(fitem->id, "http:", 5) /* id looks like an url */
						|| !strncmp(fitem->id, "https:", 6))) {
				if (!ritem->url || strcmp(ritem->url, fitem->id)) {
					/* id is different from feed url (good chance it is a permalink) */
					debug_print("RSSyl: Marking ID as permalink\n");
					fitem->id_is_permalink = TRUE;
				}
			}

			if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
				rssyl_free_feeditem(fitem);
				fitem = NULL;
			}
			count++;
		} else
			debug_print("RSSyl: Incomplete Atom entry, need at least 'id', 'title' and 'updated' tags\n");
	}

	return count;
}

--- NEW FILE: rssyl_cb_gtk.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - callback handler functions for GTK signals and timeouts
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "folderview.h"
#include "alertpanel.h"
#include "gtk/inputdialog.h"

#include "date.h"
#include "feed.h"
#include "rssyl.h"
#include "rssyl_gtk.h"
#include "rssyl_cb_gtk.h"
#include "prefs_common.h"

gboolean rssyl_props_cancel_cb(GtkWidget *widget, gpointer data)
{
	RSSylFolderItem *ritem = (RSSylFolderItem *)data;

	debug_print("RSSyl: Cancel pressed\n");
	gtk_widget_destroy(ritem->feedprop->window);
	return FALSE;
}

gboolean rssyl_props_ok_cb(GtkWidget *widget, gpointer data)
{
	RSSylFolderItem *ritem = (RSSylFolderItem *)data;

	debug_print("RSSyl: OK pressed\n");
	rssyl_gtk_prop_store(ritem);

	gtk_widget_destroy(ritem->feedprop->window);

	return FALSE;
}

gboolean rssyl_refresh_timeout_cb(gpointer data)
{
	RSSylRefreshCtx *ctx = (RSSylRefreshCtx *)data;
	time_t tt = time(NULL);
	gchar *tmpdate;

	g_return_val_if_fail(ctx != NULL, FALSE);

	if( prefs_common_get_prefs()->work_offline)
		return TRUE;

	if( ctx->ritem == NULL || ctx->ritem->url == NULL ) {
		debug_print("RSSyl: refresh_timeout_cb - ritem or url NULL\n");
		g_free(ctx);
		return FALSE;
	}

	if( ctx->id != ctx->ritem->refresh_id ) {
		tmpdate = createRFC822Date(&tt);
		debug_print(" %s: timeout id changed, stopping: %d != %d\n",
				tmpdate, ctx->id, ctx->ritem->refresh_id);
		g_free(tmpdate);
		g_free(ctx);
		return FALSE;
	}

	tmpdate = createRFC822Date(&tt);
	debug_print(" %s: refresh %s (%d)\n", tmpdate, ctx->ritem->url,
			ctx->ritem->refresh_id);
	g_free(tmpdate);
	rssyl_update_feed(ctx->ritem);

	return TRUE;
}

gboolean rssyl_default_refresh_interval_toggled_cb(GtkToggleButton *default_ri,
		gpointer data)
{
	gboolean active = gtk_toggle_button_get_active(default_ri);
	GtkWidget *refresh_interval = (GtkWidget *)data;

	debug_print("default is %s\n", ( active ? "ON" : "OFF" ) );

	gtk_widget_set_sensitive(refresh_interval, !active);

	return FALSE;
}

gboolean rssyl_default_expired_num_toggled_cb(GtkToggleButton *default_ex,
		gpointer data)
{
	gboolean active = gtk_toggle_button_get_active(default_ex);
	GtkWidget *expired_num = (GtkWidget *)data;

	debug_print("default is %s\n", ( active ? "ON" : "OFF" ) );

	gtk_widget_set_sensitive(expired_num, !active);

	return FALSE;
}

gboolean rssyl_fetch_comments_toggled_cb(GtkToggleButton *fetch_comments,
		gpointer data)
{
	gboolean active = gtk_toggle_button_get_active(fetch_comments);
	GtkWidget *num_comments = (GtkWidget *)data;

	debug_print("fetch comments is %s\n", (active ? "ON" : "OFF") );

	gtk_widget_set_sensitive(num_comments, active);

	return FALSE;
}

gboolean rssyl_props_key_press_cb(GtkWidget *widget, GdkEventKey *event,
		gpointer data)
{
	if (event) {
		switch (event->keyval) {
			case GDK_Escape:
				rssyl_props_cancel_cb(widget, data);
				return TRUE;
			case GDK_Return:
			case GDK_KP_Enter:
				rssyl_props_ok_cb(widget, data);
				return TRUE;
			default:
				break;
		}
	}

	return FALSE;
}

--- NEW FILE: rssyl_gtk.c ---
/*
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
 * This file (C) 2005 Andrej Kacian <andrej at kacian.sk>
 *
 * - GUI handling functions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include <gtk/gtk.h>

#include "gtk/menu.h"
#include "mainwindow.h"
#include "inputdialog.h"
#include "folderview.h"
#include "alertpanel.h"
#include "summaryview.h"

#include "main.h"
#include "gtkutils.h"

#include "feed.h"
#include "feedprops.h"
#include "rssyl.h"
#include "rssyl_cb_gtk.h"
#include "rssyl_cb_menu.h"
#include "rssyl_gtk.h"
#include "rssyl_prefs.h"

static char *rssyl_popup_menu_labels[] =
{
	N_("_Refresh feed"),
	N_("Refresh _all feeds"),
	N_("Subscribe _new feed..."),
	N_("_Unsubscribe feed..."),
	N_("Feed pr_operties..."),
	N_("Import feed list..."),
	N_("Rena_me..."),
	N_("_Create new folder..."),
	N_("_Delete folder..."),
	N_("Remove folder _tree..."),
	NULL
};

static GtkActionEntry rssyl_popup_entries[] = 
{
	{"FolderViewPopup/RefreshFeed",		NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_refresh_cb) },
	{"FolderViewPopup/RefreshAllFeeds",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_refresh_all_cb) },

	{"FolderViewPopup/NewFeed",		NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_feed_cb) },
	{"FolderViewPopup/RemoveFeed",		NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_feed_cb) },
	{"FolderViewPopup/FeedProperties",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_prop_cb) },
	{"FolderViewPopup/ImportFeedlist",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_import_feed_list_cb) },

	{"FolderViewPopup/RenameFolder",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_rename_cb) },

	{"FolderViewPopup/NewFolder",		NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_folder_cb) },
	{"FolderViewPopup/RemoveFolder",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_folder_cb) },

	{"FolderViewPopup/RemoveMailbox",	NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_rss_cb) },

};

static void rssyl_add_menuitems(GtkUIManager *ui_manager, FolderItem *item)
{
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RefreshFeed", "FolderViewPopup/RefreshFeed", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RefreshAllFeeds", "FolderViewPopup/RefreshAllFeeds", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "NewFeed", "FolderViewPopup/NewFeed", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveFeed", "FolderViewPopup/RemoveFeed", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "FeedProperties", "FolderViewPopup/FeedProperties", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "ImportFeedlist", "FolderViewPopup/ImportFeedlist", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS2", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RenameFolder", "FolderViewPopup/RenameFolder", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "NewFolder", "FolderViewPopup/NewFolder", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveFolder", "FolderViewPopup/RemoveFolder", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS4", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveMailbox", "FolderViewPopup/RemoveMailbox", GTK_UI_MANAGER_MENUITEM)
	MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS5", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
}

static void rssyl_set_sensitivity(GtkUIManager *ui_manager, FolderItem *item)
{
#define SET_SENS(name, sens) \
	cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)

	RSSylFolderItem *ritem = (RSSylFolderItem *)item;
	SET_SENS("FolderViewPopup/RefreshFeed", folder_item_parent(item) != NULL && ritem->url);
	SET_SENS("FolderViewPopup/RefreshAllFeeds", folder_item_parent(item) == NULL );
	SET_SENS("FolderViewPopup/NewFeed", TRUE);
	SET_SENS("FolderViewPopup/ImportFeedlist", TRUE );
	SET_SENS("FolderViewPopup/RemoveFeed", folder_item_parent(item) != NULL && ritem->url );
	SET_SENS("FolderViewPopup/FeedProperties", folder_item_parent(item) != NULL && ritem->url );
	SET_SENS("FolderViewPopup/RenameFolder", folder_item_parent(item) != NULL );
	SET_SENS("FolderViewPopup/NewFolder", TRUE );
	SET_SENS("FolderViewPopup/RemoveFolder", folder_item_parent(item) != NULL && !ritem->url );
	SET_SENS("FolderViewPopup/RemoveMailbox", folder_item_parent(item) == NULL );

#undef SET_SENS
}

static FolderViewPopup rssyl_popup =
{
	"rssyl",
	"<rssyl>",
	rssyl_popup_entries,
	G_N_ELEMENTS(rssyl_popup_entries),
	NULL, 0,
	NULL, 0, 0, NULL,
	rssyl_add_menuitems,
	rssyl_set_sensitivity
};

static void rssyl_fill_popup_menu_labels(void)
{
	gint i;

	for( i = 0; rssyl_popup_menu_labels[i] != NULL; i++ ) {
		(rssyl_popup_entries[i]).label = _(rssyl_popup_menu_labels[i]);
	}
}

static void rssyl_add_mailbox(GtkAction *action, gpointer callback_data)
{
	MainWindow *mainwin = (MainWindow *) callback_data;
	gchar *path;
	gchar *base = NULL;
	Folder *folder;

	path = input_dialog(_("Add RSS folder tree"),
			_("Enter name for a new RSS folder tree."),
			RSSYL_DEFAULT_MAILBOX);
	if( !path ) return;

	if( folder_find_from_path(path) ) {
		alertpanel_error(_("The mailbox '%s' already exists."), path);
		g_free(path);
		return;
	}

	base = g_path_get_basename(path);
	folder = folder_new(folder_get_class_from_string("rssyl"),
			base, path);
	g_free(base);

	if( folder->klass->create_tree(folder) < 0 ) {
		alertpanel_error(_("Creation of folder tree failed.\n"
				"Maybe some files already exist, or you don't have the permission "
				"to write there?"));
		folder_destroy(folder);
		return;
	}

	folder_add(folder);
	folder_scan_tree(folder, TRUE);

	folderview_set(mainwin->folderview);
}

static GtkActionEntry mainwindow_add_mailbox[] = {{
	"File/AddMailbox/RSSyl",
	NULL, N_("RSSyl..."), NULL, NULL, G_CALLBACK(rssyl_add_mailbox)
}};

static guint main_menu_id = 0;

void rssyl_gtk_init(void)
{
	MainWindow *mainwin = mainwindow_get_mainwindow();

	gtk_action_group_add_actions(mainwin->action_group, mainwindow_add_mailbox,
			1, (gpointer)mainwin);
	MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/File/AddMailbox", "RSSyl", 
			  "File/AddMailbox/RSSyl", GTK_UI_MANAGER_MENUITEM,
			  main_menu_id)


	rssyl_fill_popup_menu_labels();
	folderview_register_popup(&rssyl_popup);
}

void rssyl_gtk_done(void)
{
	MainWindow *mainwin = mainwindow_get_mainwindow();
	FolderView *folderview = NULL;
	FolderItem *fitem = NULL;

	if (mainwin == NULL || claws_is_exiting())
		return;

	folderview = mainwin->folderview;
	fitem = folderview->summaryview->folder_item;

	if( fitem && IS_RSSYL_FOLDER_ITEM(fitem) ) {
		folderview_unselect(folderview);
		summary_clear_all(folderview->summaryview);
	}

	folderview_unregister_popup(&rssyl_popup);

	MENUITEM_REMUI_MANAGER(mainwin->ui_manager,mainwin->action_group, "File/AddMailbox/RSSyl", main_menu_id);
	main_menu_id = 0;
}

/***********************************************/

static RSSylFeedProp *rssyl_gtk_prop_real(RSSylFolderItem *ritem)
{
	MainWindow *mainwin = mainwindow_get_mainwindow();
	RSSylFeedProp *feedprop;
	GtkWidget *vbox, *urllabel, *urlframe, *urlalign, *table, *refresh_label,
						*expired_label, *hsep, *sep, *bbox, *cancel_button, *cancel_align,
						*cancel_hbox, *cancel_image, *cancel_label, *ok_button, *ok_align,
						*ok_hbox, *ok_image, *ok_label, *silent_update_label;
	GtkObject *refresh_adj, *expired_adj, *fetch_comments_for_adj;
	gint refresh, expired;
	gint row = 0;

	g_return_val_if_fail(ritem != NULL, NULL);

	feedprop = g_new0(RSSylFeedProp, 1);

	/* Create required widgets */

	/* Window */
	feedprop->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "rssyl_gtk");

	/* URL entry */
	feedprop->url = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(feedprop->url), ritem->url);

	/* "Use default refresh interval" checkbutton */
	feedprop->default_refresh_interval = gtk_check_button_new_with_mnemonic(
			_("Use default refresh interval"));
	gtk_toggle_button_set_active(
			GTK_TOGGLE_BUTTON(feedprop->default_refresh_interval),
			ritem->default_refresh_interval);

	if( ritem->refresh_interval >= 0 && !ritem->default_refresh_interval )
		refresh = ritem->refresh_interval;
	else
		refresh = rssyl_prefs_get()->refresh;

	/* "Keep default number of expired items" checkbutton */
	feedprop->default_expired_num = gtk_check_button_new_with_mnemonic(
			_("Keep default number of expired entries"));
	gtk_toggle_button_set_active(
			GTK_TOGGLE_BUTTON(feedprop->default_expired_num),
			ritem->default_expired_num);

	feedprop->fetch_comments = gtk_check_button_new_with_mnemonic(
			_("Fetch comments if possible"));
	gtk_toggle_button_set_active(
			GTK_TOGGLE_BUTTON(feedprop->fetch_comments),
			ritem->fetch_comments);

	/* Refresh interval spinbutton */
	fetch_comments_for_adj = gtk_adjustment_new(ritem->fetch_comments_for,
			-1, 100000, 1, 10, 0);
	feedprop->fetch_comments_for = gtk_spin_button_new(GTK_ADJUSTMENT(fetch_comments_for_adj),
			1, 0);

	if( ritem->default_expired_num )
		expired = rssyl_prefs_get()->expired;
	else
		expired = ritem->expired_num;

	/* Refresh interval spinbutton */
	refresh_adj = gtk_adjustment_new(refresh,
			0, 100000, 1, 10, 0);
	feedprop->refresh_interval = gtk_spin_button_new(GTK_ADJUSTMENT(refresh_adj),
			1, 0);

	/* Expired num spinbutton */
	expired_adj = gtk_adjustment_new(ritem->expired_num, -1, 100000, 1, 10, 0);
	feedprop->expired_num = gtk_spin_button_new(GTK_ADJUSTMENT(expired_adj),
			1, 0);

	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(feedprop->window), vbox);

	/* URL frame */
	urlframe = gtk_frame_new(NULL);
	gtk_container_set_border_width(GTK_CONTAINER(urlframe), 5);
	gtk_frame_set_label_align(GTK_FRAME(urlframe), 0.05, 0.5);
	gtk_frame_set_shadow_type(GTK_FRAME(urlframe), GTK_SHADOW_ETCHED_OUT);
	gtk_box_pack_start(GTK_BOX(vbox), urlframe, FALSE, FALSE, 0);

	/* Label for URL frame */
	urllabel = gtk_label_new(_("<b>Source URL:</b>"));
	gtk_label_set_use_markup(GTK_LABEL(urllabel), TRUE);
	gtk_misc_set_padding(GTK_MISC(urllabel), 5, 0);
	gtk_frame_set_label_widget(GTK_FRAME(urlframe), urllabel);

	/* URL entry (+ alignment in frame) */
	urlalign = gtk_alignment_new(0, 0.5, 1, 1);
	gtk_alignment_set_padding(GTK_ALIGNMENT(urlalign), 5, 5, 5, 5);
	gtk_container_add(GTK_CONTAINER(urlframe), urlalign);

	gtk_entry_set_activates_default(GTK_ENTRY(feedprop->url), TRUE);
	gtk_container_add(GTK_CONTAINER(urlalign), feedprop->url);

	/* Table for remaining properties */
	table = gtk_table_new(8, 2, FALSE);
	gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);

	/* Fetch comments - checkbutton */
	gtk_table_attach(GTK_TABLE(table), feedprop->fetch_comments,
			0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 0);
	g_signal_connect(G_OBJECT(feedprop->fetch_comments), "toggled",
			G_CALLBACK(rssyl_fetch_comments_toggled_cb),
			(gpointer)feedprop->fetch_comments_for);
	row++;

	/* Fetch comments for - label */
	refresh_label = gtk_label_new(_("<b>Fetch comments on posts aged less than:</b>\n"
			"<small>(In days; set to -1 to fetch all comments)"
			"</small>"));
	gtk_label_set_use_markup(GTK_LABEL(refresh_label), TRUE);
	gtk_misc_set_alignment(GTK_MISC(refresh_label), 0, 0.5);
	gtk_table_attach(GTK_TABLE(table), refresh_label, 0, 1, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	/* Fetch comments for - spinbutton */
	gtk_widget_set_sensitive(feedprop->fetch_comments_for,
			ritem->fetch_comments);
	gtk_table_attach(GTK_TABLE(table), feedprop->fetch_comments_for, 1, 2, row, row+1,
			(GtkAttachOptions) (0),
			(GtkAttachOptions) (0), 10, 5);

	row++;
	hsep = gtk_hseparator_new();
	gtk_widget_set_size_request(hsep, -1, 10);
	gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	row++;
	/* Use default refresh interval - checkbutton */
	gtk_table_attach(GTK_TABLE(table), feedprop->default_refresh_interval,
			0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 0);
	g_signal_connect(G_OBJECT(feedprop->default_refresh_interval), "toggled",
			G_CALLBACK(rssyl_default_refresh_interval_toggled_cb),
			(gpointer)feedprop->refresh_interval);
	row++;
	/* Refresh interval - label */
	refresh_label = gtk_label_new(_("<b>Refresh interval in minutes:</b>\n"
			"<small>(Set to 0 to disable automatic refreshing for this feed)"
			"</small>"));
	gtk_label_set_use_markup(GTK_LABEL(refresh_label), TRUE);
	gtk_misc_set_alignment(GTK_MISC(refresh_label), 0, 0.5);
	gtk_table_attach(GTK_TABLE(table), refresh_label, 0, 1, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	/* Refresh interval - spinbutton */
	gtk_widget_set_sensitive(feedprop->refresh_interval,
			!ritem->default_refresh_interval);
	gtk_table_attach(GTK_TABLE(table), feedprop->refresh_interval, 1, 2, row, row+1,
			(GtkAttachOptions) (0),
			(GtkAttachOptions) (0), 10, 5);
	row++;
	hsep = gtk_hseparator_new();
	gtk_widget_set_size_request(hsep, -1, 10);
	gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	row++;
	/* Use default number for expired - checkbutton */
	gtk_table_attach(GTK_TABLE(table), feedprop->default_expired_num,	0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 0);
	g_signal_connect(G_OBJECT(feedprop->default_expired_num), "toggled",
			G_CALLBACK(rssyl_default_expired_num_toggled_cb),
			(gpointer)feedprop->expired_num);

	row++;
	/* Expired items - label */
	expired_label = gtk_label_new(_("<b>Number of expired entries to keep:"
			"</b>\n<small>(Set to -1 if you want to keep expired entries)"
			"</small>"));
	gtk_label_set_use_markup(GTK_LABEL(expired_label), TRUE);
	gtk_misc_set_alignment(GTK_MISC(expired_label), 0, 0.5);
	gtk_table_attach(GTK_TABLE(table), expired_label, 0, 1, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	/* Expired items - spinbutton */
	gtk_widget_set_sensitive(feedprop->expired_num,
			!ritem->default_expired_num);
	gtk_table_attach(GTK_TABLE(table), feedprop->expired_num, 1, 2, row, row+1,
			(GtkAttachOptions) (0),
			(GtkAttachOptions) (0), 10, 5);

	row++;
	hsep = gtk_hseparator_new();
	gtk_widget_set_size_request(hsep, -1, 10);
	gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	row++;
	/* Silent update - label */
	silent_update_label =
		gtk_label_new(_("<b>If an item changes, do not mark it as unread:</b>"));
	gtk_label_set_use_markup(GTK_LABEL(silent_update_label), TRUE);
	gtk_misc_set_alignment(GTK_MISC(silent_update_label), 0, 0.5);
	gtk_table_attach(GTK_TABLE(table), silent_update_label, 0, 1, row, row+1,
			(GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
			(GtkAttachOptions) (0), 10, 5);

	/* Silent update - combobox */
	feedprop->silent_update = gtk_combo_box_new_text();
	gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
			_("Always mark as unread"));
	gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
			_("If only its text changed"));
	gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
			_("Never mark as unread"));
	gtk_table_attach(GTK_TABLE(table), feedprop->silent_update, 1, 2, row, row+1,
			(GtkAttachOptions) (0),
			(GtkAttachOptions) (0), 10, 5);
	gtk_combo_box_set_active(GTK_COMBO_BOX(feedprop->silent_update),
			ritem->silent_update);

	/* Separator above the button box */
	sep = gtk_hseparator_new();
	gtk_widget_set_size_request(sep, -1, 10);
	gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);

	/* Buttonbox */
	bbox = gtk_hbutton_box_new();
	gtk_container_set_border_width(GTK_CONTAINER(bbox), 5);
	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
	gtk_box_set_spacing(GTK_BOX(bbox), 5);
	gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);

	/* Cancel button */
	cancel_button = gtk_button_new();
	gtk_container_add(GTK_CONTAINER(bbox), cancel_button);

	cancel_align = gtk_alignment_new(0.5, 0.5, 0, 0);
	gtk_container_add(GTK_CONTAINER(cancel_button), cancel_align);

	cancel_hbox = gtk_hbox_new(FALSE, 2);
	gtk_container_add(GTK_CONTAINER(cancel_align), cancel_hbox);

	cancel_image = gtk_image_new_from_stock(GTK_STOCK_CANCEL,
			GTK_ICON_SIZE_BUTTON);
	gtk_box_pack_start(GTK_BOX(cancel_hbox), cancel_image, FALSE, FALSE, 0);

	cancel_label = gtk_label_new_with_mnemonic(_("_Cancel"));
	gtk_box_pack_end(GTK_BOX(cancel_hbox), cancel_label, FALSE, FALSE, 0);

	g_signal_connect(G_OBJECT(cancel_button), "clicked",
			G_CALLBACK(rssyl_props_cancel_cb), ritem);

	/* OK button */
	ok_button = gtk_button_new();
	gtk_container_add(GTK_CONTAINER(bbox), ok_button);
	gtkut_widget_set_can_default(ok_button, TRUE);

	ok_align = gtk_alignment_new(0.5, 0.5, 0, 0);
	gtk_container_add(GTK_CONTAINER(ok_button), ok_align);

	ok_hbox = gtk_hbox_new(FALSE, 2);
	gtk_container_add(GTK_CONTAINER(ok_align), ok_hbox);

	ok_image = gtk_image_new_from_stock(GTK_STOCK_OK,
			GTK_ICON_SIZE_BUTTON);
	gtk_box_pack_start(GTK_BOX(ok_hbox), ok_image, FALSE, FALSE, 0);

	ok_label = gtk_label_new_with_mnemonic(_("_OK"));
	gtk_box_pack_end(GTK_BOX(ok_hbox), ok_label, FALSE, FALSE, 0);

	g_signal_connect(G_OBJECT(ok_button), "clicked",
			G_CALLBACK(rssyl_props_ok_cb), ritem);

	/* Set some misc. stuff */
	gtk_window_set_title(GTK_WINDOW(feedprop->window),
			g_strdup(_("Set feed properties")) );
	gtk_window_set_modal(GTK_WINDOW(feedprop->window), TRUE);
	gtk_window_set_transient_for(GTK_WINDOW(feedprop->window),
			GTK_WINDOW(mainwin->window) );

	/* Attach callbacks to handle Enter and Escape keys */
	g_signal_connect(G_OBJECT(feedprop->window), "key_press_event",
			G_CALLBACK(rssyl_props_key_press_cb), ritem);

	/* ...and voila! */
	gtk_widget_show_all(feedprop->window);
	gtk_widget_grab_default(ok_button);

	/* Unselect the text in URL entry */
	gtk_editable_select_region(GTK_EDITABLE(feedprop->url), 0, 0);

	ritem->feedprop = feedprop;

	return feedprop;
}

void rssyl_gtk_prop(RSSylFolderItem *ritem)
{
	g_return_if_fail(ritem != NULL);

	rssyl_gtk_prop_real(ritem);
}

void rssyl_gtk_prop_store(RSSylFolderItem *ritem)
{
	gchar *url;
	gint x, old_ri, old_ex, old_fetch_comments;
	gboolean use_default_ri = FALSE, use_default_ex = FALSE;

	g_return_if_fail(ritem != NULL);
	g_return_if_fail(ritem->feedprop != NULL);

	url = (gchar *)gtk_entry_get_text(GTK_ENTRY(ritem->feedprop->url));

	if( strlen(url) ) {
		if( ritem->url ) {
			g_free(ritem->url);
		}
		ritem->url = g_strdup(url);
	}

	use_default_ri = gtk_toggle_button_get_active(
			GTK_TOGGLE_BUTTON(ritem->feedprop->default_refresh_interval));
	ritem->default_refresh_interval = use_default_ri;
	debug_print("store: default is %s\n", ( use_default_ri ? "ON" : "OFF" ) );

	/* Use default if checkbutton is set */
	if( use_default_ri )
		x = rssyl_prefs_get()->refresh;
	else
		x = gtk_spin_button_get_value_as_int(
				GTK_SPIN_BUTTON(ritem->feedprop->refresh_interval) );

	old_ri = ritem->refresh_interval;
	ritem->refresh_interval = x;

	/* Update refresh interval setting if it has changed and has a sane value */
	if( old_ri != x && x >= 0 ) {
		debug_print("RSSyl: GTK - refresh interval changed to %d , updating "
				"timeout\n", ritem->refresh_interval);
		/* Value of 0 means we do not want to update automatically */
		if( x > 0 )
			rssyl_start_refresh_timeout(ritem);
	}

	old_fetch_comments = ritem->fetch_comments;
	ritem->fetch_comments = gtk_toggle_button_get_active(
			GTK_TOGGLE_BUTTON(ritem->feedprop->fetch_comments));

	ritem->fetch_comments_for = gtk_spin_button_get_value_as_int(
				GTK_SPIN_BUTTON(ritem->feedprop->fetch_comments_for));

	if (!old_fetch_comments && ritem->fetch_comments) {
		/* reset the RSSylFolderItem's mtime to be sure we get all 
		 * available comments */
		 ritem->item.mtime = 0;
	}
	
	use_default_ex = gtk_toggle_button_get_active(
			GTK_TOGGLE_BUTTON(ritem->feedprop->default_expired_num));
	ritem->default_expired_num = use_default_ex;
	debug_print("store: default is %s\n", ( use_default_ex ? "ON" : "OFF" ) );

	/* Use default if checkbutton is set */
	if( use_default_ex )
		x = rssyl_prefs_get()->expired;
	else
		x = gtk_spin_button_get_value_as_int(
				GTK_SPIN_BUTTON(ritem->feedprop->expired_num) );

	old_ex = ritem->expired_num;
	ritem->expired_num = x;

	ritem->silent_update =
		gtk_combo_box_get_active(GTK_COMBO_BOX(ritem->feedprop->silent_update));
	if( ritem->silent_update < 0 )
		ritem->silent_update = 0;

	rssyl_store_feed_props(ritem);

	debug_print("last_count %d, x %d, old_ex %d\n", ritem->last_count, x, old_ex);

	/* update if new setting is lower, or if it was changed from -1 */
	if( ritem->last_count != 0 && x != -1 && (old_ex > x || old_ex == -1) ) {
		debug_print("RSSyl: GTK - expired_num has changed to %d, expiring\n",
				ritem->expired_num);
		rssyl_expire_items(ritem);
	}
}

GtkWidget *rssyl_feed_removal_dialog(gchar *name, GtkWidget **rmcache_widget)
{
	gchar *message;
	GtkWidget *dialog, *vbox, *hbox, *image, *vbox2, *label, *cb, *aa,
						*bno, *byes;
	MainWindow *mainwin = mainwindow_get_mainwindow();

	g_return_val_if_fail(name != NULL, NULL);

	dialog = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(dialog), _("Unsubscribe feed"));
	gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);

	vbox = GTK_DIALOG(dialog)->vbox;

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);

	/* Question icon */
	image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
			GTK_ICON_SIZE_DIALOG);
	gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.30);
	gtk_misc_set_padding(GTK_MISC(image), 12, 0);
	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);

	vbox2 = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);

	/* Dialog text label */
	label = gtk_label_new("");
	gtk_misc_set_alignment(GTK_MISC(label), 0.1, 0);
	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
	gtk_misc_set_padding(GTK_MISC(label), 0, 12);
	message = g_markup_printf_escaped("<span size='x-large'><b>%s</b></span>"
			"\n\n%s '%s' ?", _("Unsubscribe feed"),
			_("Do you really want to remove feed"), name);
	gtk_label_set_markup(GTK_LABEL(label), message);
	g_free(message);
	gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0);

	/* Remove cache checkbutton */
	cb = gtk_check_button_new_with_mnemonic(_("Remove cached entries"));
	gtk_button_set_focus_on_click(GTK_BUTTON(cb), FALSE);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox2), cb, FALSE, FALSE, 0);
	*rmcache_widget = cb;

	aa = GTK_DIALOG(dialog)->action_area;
	gtk_button_box_set_layout(GTK_BUTTON_BOX(aa), GTK_BUTTONBOX_END);

	bno = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
	gtk_dialog_add_action_widget(GTK_DIALOG(dialog), bno, GTK_RESPONSE_NO);
	gtkut_widget_set_can_default(bno, TRUE);
	
	byes = gtk_button_new_with_mnemonic(_("_Unsubscribe"));
	gtk_dialog_add_action_widget(GTK_DIALOG(dialog), byes, GTK_RESPONSE_YES);

	gtk_widget_grab_default(bno);
	gtk_window_set_transient_for(GTK_WINDOW(dialog),
			GTK_WINDOW(mainwin->window) );

	return dialog;
}

--- NEW FILE: opml.h ---
#ifndef __RSSYL_OPML
#define __RSSYL_OPML

#include "rssyl.h"

void rssyl_opml_export(void);
void rssyl_opml_import(const gchar *opmlfile, FolderItem *parent);

#endif /* __RSSYL_OPML */

--- NEW FILE: rssyl_gtk.h ---
#ifndef __RSSYL_GTK
#define __RSSYL_GTK

#include <gtk/gtk.h>

#include <folder.h>

#include "rssyl.h"

struct _RSSylFeedProp {
	GtkWidget *window;
	GtkWidget *url;
	GtkWidget *default_refresh_interval;
	GtkWidget *refresh_interval;
	GtkWidget *default_expired_num;
	GtkWidget *expired_num;
	GtkWidget *fetch_comments;
	GtkWidget *fetch_comments_for;
	GtkWidget *silent_update;
};

typedef struct _RSSylFeedProp RSSylFeedProp;

void rssyl_gtk_init(void);
void rssyl_gtk_done(void);

void rssyl_gtk_prop(RSSylFolderItem *ritem);
void rssyl_gtk_prop_store(RSSylFolderItem *ritem);

GtkWidget *rssyl_feed_removal_dialog(gchar *name, GtkWidget **rmcache_widget);

#endif /* __RSSYL_GTK */

--- NEW FILE: opml.c ---
/*
 * Claws-Mail-- a GTK+ based, lightweight, and fast e-mail client
 * This file (C) 2008 Andrej Kacian <andrej at kacian.sk>
 *
 * - Export feed list to OPML
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <errno.h>
#include <glib.h>

#include <libxml/parser.h>
#include <libxml/xpath.h>

#include "log.h"
#include "folder.h"
#include "folderview.h"

#include "date.h"
#include "feed.h"
#include "rssyl.h"
#include "strreplace.h"

#define RSSYL_OPML_FILE	"rssyl-feedlist.opml"

static gint _folder_depth(FolderItem *item)
{
	gint i;

	for( i = 0; item != NULL; item = folder_item_parent(item), i++ ) {}
	return i;
}

struct _RSSylOpmlExportCtx {
	FILE *f;
	gint depth;
};

typedef struct _RSSylOpmlExportCtx RSSylOpmlExportCtx;

static void rssyl_opml_export_func(FolderItem *item, gpointer data)
{
	RSSylOpmlExportCtx *ctx = (RSSylOpmlExportCtx *)data;
	RSSylFolderItem *ritem = (RSSylFolderItem *)item;
	gboolean isfolder = FALSE, err = FALSE;
	gboolean haschildren = FALSE;
	gchar *indent = NULL, *xmlurl = NULL;
	gchar *tmpoffn = NULL, *tmpurl = NULL, *tmpname = NULL;
	gint depth;

	if( !IS_RSSYL_FOLDER_ITEM(item) )
		return;

	if( folder_item_parent(item) == NULL )
		return;

	/* Check for depth and adjust indentation */
	depth = _folder_depth(item);
	if( depth < ctx->depth ) {
		for( ctx->depth--; depth <= ctx->depth; ctx->depth-- ) {
			indent = g_strnfill(ctx->depth, '\t');
			err |= (fprintf(ctx->f, "%s</outline>\n", indent) < 0);
			g_free(indent);
		}
	}
	ctx->depth = depth;

	if( ritem->url == NULL ) {
		isfolder = TRUE;
	} else {
		tmpurl = rssyl_strreplace(ritem->url, "&", "&");
		xmlurl = g_strdup_printf("xmlUrl=\"%s\"", tmpurl);
		g_free(tmpurl);
	}

	if( g_node_n_children(item->node) )
		haschildren = TRUE;

	indent = g_strnfill(ctx->depth, '\t');

	tmpname = rssyl_strreplace(item->name, "&", "&");
	if (ritem->official_name != NULL)
		tmpoffn = rssyl_strreplace(item->name, "&", "&");
	else
		tmpoffn = g_strdup(tmpname);

	err |= (fprintf(ctx->f,
				"%s<outline title=\"%s\" text=\"%s\" description=\"%s\" type=\"%s\" %s%s>\n",
				indent, tmpname, tmpoffn, tmpoffn,
				(isfolder ? "folder" : "rss"),
				(xmlurl ? xmlurl : ""), (haschildren ? "" : "/")) < 0);

	g_free(indent);
	g_free(xmlurl);
	g_free(tmpname);
	g_free(tmpoffn);
	
	if( err ) {
		log_warning(LOG_PROTOCOL,
				"Error while writing '%s' to feed export list.\n",
				item->name);
		debug_print("Error while writing '%s' to feed_export list.\n",
				item->name);
	}
}

void rssyl_opml_export(void)
{
	FILE *f;
	gchar *opmlfile, *tmpdate, *indent;
	time_t tt = time(NULL);
	RSSylOpmlExportCtx *ctx = NULL;
	gboolean err = FALSE;

	opmlfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
			G_DIR_SEPARATOR_S, RSSYL_OPML_FILE, NULL);

	if( g_file_test(opmlfile, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR ) )
		g_remove(opmlfile);
	
	if( (f = g_fopen(opmlfile, "w")) == NULL ) {
		log_warning(LOG_PROTOCOL,
				"Couldn't open file '%s' for feed list exporting: %s\n",
				opmlfile, g_strerror(errno));
		debug_print("Couldn't open feed list export file, returning.\n");
		g_free(opmlfile);
		return;
	}

	tmpdate = createRFC822Date(&tt);

	/* Write OPML header */
	err |= (fprintf(f,
				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
				"<opml version=\"1.1\">\n"
				"\t<head>\n"
				"\t\t<title>RSSyl Feed List Export</title>\n"
				"\t\t<dateCreated>%s</dateCreated>\n"
				"\t</head>\n"
				"\t<body>\n",
				tmpdate) < 0);
	g_free(tmpdate);

	ctx = g_new0(RSSylOpmlExportCtx, 1);
	ctx->f = f;
	ctx->depth = 1;

	folder_func_to_all_folders(
			(FolderItemFunc)rssyl_opml_export_func, ctx);

	for( ctx->depth--; ctx->depth >= 2; ctx->depth-- ) {
		indent = g_strnfill(ctx->depth, '\t');
		err |= (fprintf(ctx->f, "%s</outline>\n", indent) < 0);
		g_free(indent);
	}

	err |= (fprintf(f,
				"\t</body>\n"
				"</opml>\n") < 0);

	if( err ) {
		log_warning(LOG_PROTOCOL, "Error during writing feed export file.\n");
		debug_print("Error during writing feed export file.");
	}

	debug_print("Feed export finished.\n");

	fclose(f);
	g_free(opmlfile);
	g_free(ctx);
}

static void rssyl_opml_import_node(xmlNodePtr node,
		FolderItem *parent, gint depth)
{
	xmlNodePtr curn;
	gchar *url = NULL, *title = NULL, *nodename = NULL;
	FolderItem *item = NULL;

	if( node == NULL )
		return;

	for( curn = node; curn; curn = curn->next ) {
		nodename = g_ascii_strdown((gchar *)curn->name, -1);
		if( curn->type == XML_ELEMENT_NODE &&
				!strcmp(nodename, "outline") ) {

			url = (gchar *)xmlGetProp(curn, (xmlChar *)"xmlUrl");
			title = (gchar *)xmlGetProp(curn, (xmlChar *)"title");
			if (!title)
				title = (gchar *)xmlGetProp(curn, (xmlChar *)"text");
			
			debug_print("Adding '%s' (%s)\n", title, (url ? url : "folder") );
			if( url != NULL )
				item = rssyl_subscribe_new_feed(parent, url, FALSE);
			else if (title != NULL)
				item = folder_create_folder(parent, title);
			else
				item = NULL;
			if (item)
				rssyl_opml_import_node(curn->children, item, depth + 1);
		}
		g_free(nodename);
	}
}

void rssyl_opml_import(const gchar *opmlfile, FolderItem *parent)
{
	xmlDocPtr doc;
	xmlNodePtr node;
	xmlXPathContextPtr context;
	xmlXPathObjectPtr result;
	gchar *rootnode = NULL;

	doc = xmlParseFile(opmlfile);
	if( doc == NULL )
		return;

	node = xmlDocGetRootElement(doc);
	rootnode = g_ascii_strdown((gchar *)node->name, -1);
	if( !strcmp(rootnode, "opml") ) {
		gchar *xpath = "/opml/body";
		context = xmlXPathNewContext(doc);
		if( !(result = xmlXPathEval((xmlChar *)xpath, context)) ) {
			g_free(rootnode);
			xmlFreeDoc(doc);
			return;
		}

		node = result->nodesetval->nodeTab[0];

		debug_print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
		rssyl_opml_import_node(node->children, parent, 2);
		debug_print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");

		xmlXPathFreeNodeSetList(result);
		xmlXPathFreeContext(context);
		xmlFreeDoc(doc);
	}

	g_free(rootnode);
}



More information about the Commits mailing list