[Commits] [SCM] claws branch, new-rssyl, created. 3.10.0-66-g6f7b8b2
ticho at claws-mail.org
ticho at claws-mail.org
Sat Jun 7 20:56:16 CEST 2014
The branch new-rssyl of project "claws" (Claws Mail) has been created
at 6f7b8b2c476af720b8d68b64e05b716bacd5896c (commit)
- Log -----------------------------------------------------------------
commit 6f7b8b2c476af720b8d68b64e05b716bacd5896c
Author: Andrej Kacian <ticho at claws-mail.org>
Date: Mon Jun 2 00:24:21 2014 +0200
New RSSyl replacing old one.
diff --git a/configure.ac b/configure.ac
index 281a4ad..f47031f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1055,7 +1055,7 @@ dnl PGP/Mime: pgpcore libgpgme
dnl PGP/Inline: pgpcore libgpgme
dnl S/Mime: pgpcore libgpgme
dnl Python: Python
-dnl RSSyl: libxml2 libcurl
+dnl RSSyl: expat libcurl
dnl SpamReport: libcurl
dnl vCalendar: libcurl
@@ -1064,10 +1064,14 @@ PKG_CHECK_MODULES(CURL, libcurl, HAVE_CURL=yes, HAVE_CURL=no)
AC_SUBST(CURL_LIBS)
AC_SUBST(CURL_CFLAGS)
-dnl libxml2 ********************************************************************
-PKG_CHECK_MODULES(LIBXML, libxml-2.0, HAVE_LIBXML=yes, HAVE_LIBXML=no)
-AC_SUBST(LIBXML_LIBS)
-AC_SUBST(LIBXML_CFLAGS)
+dnl expat **********************************************************************
+HAVE_EXPAT=0
+AC_CHECK_HEADER(expat.h, [expat_header=yes], [])
+AC_CHECK_LIB(expat, XML_ParserCreate, [expat_lib=yes], [])
+if test x"$expat_header" = xyes -a x"$expat_lib"=xyes; then
+ AC_DEFINE(HAVE_EXPAT, 1, [Define if expat is available])
+ EXPAT_LIBS="-lexpat"
+fi
dnl webkit *********************************************************************
PKG_CHECK_MODULES(WEBKIT, webkit-1.0 >= 1.1.14, HAVE_WEBKIT=yes, HAVE_WEBKIT=no)
@@ -1721,8 +1725,8 @@ AC_MSG_CHECKING([whether to build rssyl plugin])
if test x"$enable_rssyl_plugin" != xno; then
dependencies_missing=""
- if test x"$HAVE_LIBXML" = xno; then
- dependencies_missing="libxml2 $dependencies_missing"
+ if test x"$HAVE_EXPAT" = xno; then
+ dependencies_missing="expat $dependencies_missing"
fi
if test x"$HAVE_CURL" = xno; then
dependencies_missing="libcurl $dependencies_missing"
@@ -1923,6 +1927,7 @@ src/plugins/pgpcore/Makefile
src/plugins/pgpmime/Makefile
src/plugins/pgpinline/Makefile
src/plugins/rssyl/Makefile
+src/plugins/rssyl/libfeed/Makefile
src/plugins/smime/Makefile
src/plugins/spamassassin/Makefile
src/plugins/spam_report/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2013668..55c4488 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -128,14 +128,23 @@ src/plugins/pgpinline/pgpinline.c
src/plugins/pgpinline/plugin.c
src/plugins/pgpmime/pgpmime.c
src/plugins/pgpmime/plugin.c
-src/plugins/rssyl/rssyl_prefs.c
-src/plugins/rssyl/rssyl.h
-src/plugins/rssyl/rssyl_gtk.c
+src/plugins/rssyl/old_feeds.c
+src/plugins/rssyl/opml_import.c
src/plugins/rssyl/plugin.c
-src/plugins/rssyl/feed.h
-src/plugins/rssyl/rssyl_cb_menu.c
+src/plugins/rssyl/rssyl_add_item.c
src/plugins/rssyl/rssyl.c
-src/plugins/rssyl/feed.c
+src/plugins/rssyl/rssyl_cb_menu.c
+src/plugins/rssyl/rssyl_feed.c
+src/plugins/rssyl/rssyl_feed.h
+src/plugins/rssyl/rssyl_feed_props.c
+src/plugins/rssyl/rssyl_gtk.c
+src/plugins/rssyl/rssyl.h
+src/plugins/rssyl/rssyl_prefs.c
+src/plugins/rssyl/rssyl_subscribe.c
+src/plugins/rssyl/rssyl_subscribe_gtk.c
+src/plugins/rssyl/rssyl_update_comments.c
+src/plugins/rssyl/rssyl_update_feed.c
+src/plugins/rssyl/rssyl_update_format.c
src/plugins/smime/plugin.c
src/plugins/smime/smime.c
src/plugins/spam_report/spam_report_prefs.c
diff --git a/src/plugins/rssyl/Makefile.am b/src/plugins/rssyl/Makefile.am
index 7887963..64417c9 100644
--- a/src/plugins/rssyl/Makefile.am
+++ b/src/plugins/rssyl/Makefile.am
@@ -2,6 +2,8 @@
# This file is part of Claws Mail package.
# See COPYING file for license details.
+SUBDIRS = libfeed
+
EXTRA_DIST = claws.def plugin.def version.rc
IFLAGS = \
@@ -62,29 +64,35 @@ rssyl_la_LDFLAGS = \
-avoid-version -module \
$(GTK_LIBS)
-rssyl_la_DEPENDENCIES = $(plugin_deps)
+rssyl_la_DEPENDENCIES = $(plugin_deps) libfeed/libfeed.la
rssyl_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
- $(GTK_LIBS) $(LIBXML_LIBS) $(CURL_LIBS)
+ $(GTK_LIBS) $(CURL_LIBS) libfeed/libfeed.la
rssyl_la_CPPFLAGS = \
$(IFLAGS) \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
- $(LIBXML_CFLAGS) \
$(CURL_CFLAGS)
rssyl_la_SOURCES = \
- date.c date.h \
- feed.c feed.h \
- feedprops.c feedprops.h \
- opml.c opml.h \
- parsers.c parsers.h \
+ old_feeds.c old_feeds.h \
+ opml_export.c opml_export.h \
+ opml_import.c opml_import.h \
+ parse822.c parse822.h \
plugin.c \
rssyl.c rssyl.h \
- rssyl_cb_gtk.c rssyl_cb_gtk.h \
+ rssyl_add_item.c rssyl_add_item.h \
rssyl_cb_menu.c rssyl_cb_menu.h \
+ rssyl_deleted.c rssyl_deleted.h \
+ rssyl_feed.c rssyl_feed.h \
+ rssyl_feed_props.c rssyl_feed_props.h \
rssyl_gtk.c rssyl_gtk.h \
+ rssyl_parse_feed.c rssyl_parse_feed.h \
rssyl_prefs.c rssyl_prefs.h \
- strreplace.c strreplace.h
-
+ rssyl_update_comments.c rssyl_update_comments.h \
+ rssyl_update_feed.c rssyl_update_feed.h \
+ rssyl_update_format.c rssyl_update_format.h \
+ rssyl_subscribe.c rssyl_subscribe.h \
+ rssyl_subscribe_gtk.c rssyl_subscribe_gtk.h \
+ strutils.c strutils.h
diff --git a/src/plugins/rssyl/feed.c b/src/plugins/rssyl/feed.c
deleted file mode 100644
index 8542bc7..0000000
--- a/src/plugins/rssyl/feed.c
+++ /dev/null
@@ -1,1869 +0,0 @@
-/*
- * 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
- * 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 <curl/curl.h>
-#include <curl/curlver.h>
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-#include <pthread.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "common/claws.h"
-#include "common/version.h"
-#include "codeconv.h"
-#include "procmsg.h"
-#include "procheader.h"
-#include "alertpanel.h"
-#include "folder.h"
-#include "mainwindow.h"
-#include "statusbar.h"
-#include "log.h"
-#include "prefs_common.h"
-#include "defs.h"
-#include "inc.h"
-#include "common/utils.h"
-#include "main.h"
-
-#include "date.h"
-#include "rssyl.h"
-#include "rssyl_cb_gtk.h"
-#include "feed.h"
-#include "feedprops.h"
-#include "strreplace.h"
-#include "parsers.h"
-#include "rssyl_prefs.h"
-
-static int rssyl_curl_progress_function(void *clientp,
- double dltotal, double dlnow, double ultotal, double ulnow)
-{
- if (claws_is_exiting()) {
- debug_print("RSSyl: curl_progress_function bailing out, app is exiting\n");
- return 1;
- }
-
- return 0;
-}
-
-struct _RSSylThreadCtx {
- const gchar *url;
- time_t last_update;
- gboolean not_modified;
- gboolean defer_modified_check;
- gboolean ready;
- gchar *error;
- gboolean ssl_verify_peer;
-};
-
-typedef struct _RSSylThreadCtx RSSylThreadCtx;
-
-static void *rssyl_fetch_feed_threaded(void *arg)
-{
- RSSylThreadCtx *ctx = (RSSylThreadCtx *)arg;
- CURL *eh = NULL;
- CURLcode res;
- time_t last_modified;
- gchar *time_str = NULL;
- long response_code;
-
-#ifndef G_OS_WIN32
- gchar *template = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, RSSYL_TMP_TEMPLATE, NULL);
- int fd = mkstemp(template);
-#else
- gchar *template = get_tmp_file();
- int fd = open(template, (O_CREAT|O_RDWR|O_BINARY), (S_IRUSR|S_IWUSR));
-#endif
- FILE *f;
-
-#ifdef USE_PTHREAD
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
- pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-#endif
-
- if (fd == -1) {
- perror("mkstemp");
- ctx->ready = TRUE;
- g_free(template);
- return NULL;
- }
-
- f = fdopen(fd, "w");
- if (f == NULL) {
- perror("fdopen");
- ctx->error = g_strdup(_("Cannot open temporary file"));
- claws_unlink(template);
- g_free(template);
- ctx->ready = TRUE;
- return NULL;
- }
-
- eh = curl_easy_init();
-
- if (eh == NULL) {
- g_warning("can't init curl");
- ctx->error = g_strdup(_("Cannot init libCURL"));
- fclose(f);
- claws_unlink(template);
- g_free(template);
- ctx->ready = TRUE;
- return NULL;
- }
-
- debug_print("TEMPLATE: %s\n", template);
-
- curl_easy_setopt(eh, CURLOPT_URL, ctx->url);
- curl_easy_setopt(eh, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(eh, CURLOPT_PROGRESSFUNCTION, rssyl_curl_progress_function);
-#if LIBCURL_VERSION_NUM < 0x071000
- curl_easy_setopt(eh, CURLOPT_MUTE, 1);
-#endif
- curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, NULL);
- curl_easy_setopt(eh, CURLOPT_WRITEDATA, f);
- curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1);
- curl_easy_setopt(eh, CURLOPT_MAXREDIRS, 3);
- curl_easy_setopt(eh, CURLOPT_TIMEOUT, prefs_common_get_prefs()->io_timeout_secs);
- curl_easy_setopt(eh, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(eh, CURLOPT_ENCODING, "");
-#if LIBCURL_VERSION_NUM >= 0x070a00
- if(ctx->ssl_verify_peer == FALSE) {
- curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
- curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);
- }
-#endif
- curl_easy_setopt(eh, CURLOPT_USERAGENT,
- "Claws Mail RSSyl plugin "VERSION
- " (" PLUGINS_URI ")");
-#ifdef RSSYL_DEBUG
- curl_easy_setopt(eh, CURLOPT_VERBOSE, 1);
-#endif
- curl_easy_setopt(eh, CURLOPT_COOKIEFILE,
- rssyl_prefs_get()->cookies_path);
-
- if( !ctx->defer_modified_check ) {
- if( ctx->last_update != -1 ) {
- time_str = createRFC822Date(&ctx->last_update);
- }
- debug_print("RSSyl: last update %ld (%s)\n",
- (long int)ctx->last_update,
- (ctx->last_update != -1 ? time_str : "unknown") );
- g_free(time_str);
- time_str = NULL;
- if( ctx->last_update != -1 ) {
- curl_easy_setopt(eh, CURLOPT_TIMECONDITION,
- CURL_TIMECOND_IFMODSINCE);
- curl_easy_setopt(eh, CURLOPT_TIMEVALUE, ctx->last_update);
- }
- }
-
- res = curl_easy_perform(eh);
-
- if (res != 0) {
- if (res == CURLE_OPERATION_TIMEOUTED) {
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_TIMEOUT, ctx->url);
- } else if (res == CURLE_ABORTED_BY_CALLBACK) {
- log_print(LOG_PROTOCOL, RSSYL_LOG_EXITING);
- }
- ctx->error = g_strdup(curl_easy_strerror(res));
- ctx->ready = TRUE;
- curl_easy_cleanup(eh);
- fclose(f);
- claws_unlink(template);
- g_free(template);
- return NULL;
- }
- curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &response_code);
-
- if( !ctx->defer_modified_check ) {
- if( ctx->last_update != -1 ) {
- curl_easy_getinfo(eh, CURLINFO_FILETIME, &last_modified);
-
- if( last_modified != -1 ) {
- time_str = createRFC822Date(&last_modified);
- }
- debug_print("RSSyl: got status %ld, last mod %ld (%s)\n",
- response_code, (long int) last_modified,
- (last_modified != -1 ? time_str : "unknown") );
- g_free(time_str);
- time_str = NULL;
- } else {
- debug_print("RSSyl: got status %ld\n", response_code);
- }
- }
-
- curl_easy_cleanup(eh);
-
- fclose(f);
-
- if( response_code >= 400 && response_code < 500 ) {
- debug_print("RSSyl: got %ld\n", response_code);
- switch(response_code) {
- case 401:
- ctx->error = g_strdup(_("401 (Authorisation required)"));
- break;
- case 403:
- ctx->error = g_strdup(_("403 (Unauthorised)"));
- break;
- case 404:
- ctx->error = g_strdup(_("404 (Not found)"));
- break;
- default:
- ctx->error = g_strdup_printf(_("Error %ld"), response_code);
- break;
- }
- ctx->ready = TRUE;
- claws_unlink(template);
- g_free(template);
- return NULL;
- }
-
- if( !ctx->defer_modified_check ) {
- if( response_code == 304 ) {
- debug_print("RSSyl: don't rely on server response 304, defer modified "
- "check\n");
- claws_unlink(template);
- g_free(template);
- ctx->defer_modified_check = TRUE;
- template = rssyl_fetch_feed_threaded(ctx);
- return template;
- }
- }
- ctx->ready = TRUE;
-
- return template;
-}
-
-gchar *rssyl_feed_title_to_dir(const gchar *title)
-{
-#ifndef G_OS_WIN32
- return rssyl_strreplace(title, "/", "\\");
-#else
- gchar *patterns[] = { "/", "\\", ":", "*", "?" , "\"", "<", ">", "|" };
- gchar *sanitized = g_strdup(title);
- int i;
-
- for (i = 0; i < sizeof(patterns)/sizeof(patterns[0]); i++) {
- gchar *tmp = rssyl_strreplace(sanitized, patterns[i], "-");
- g_free(sanitized);
- sanitized = tmp;
- }
-
- return sanitized;
-#endif
-}
-
-/* rssyl_fetch_feed()
- *
- * This function utilizes libcurl's easy interface to fetch the feed, pre-parse
- * it for title, create a directory based on the title. It returns a xmlDocPtr
- * for libxml2 to parse further.
- */
-xmlDocPtr rssyl_fetch_feed(const gchar *url, time_t last_update, gboolean ssl_verify_peer, gchar **title, gchar **error) {
- gchar *xpath, *rootnode, *dir;
- xmlDocPtr doc;
- xmlNodePtr node, n, rnode;
- xmlXPathContextPtr context;
- xmlXPathObjectPtr result;
- MainWindow *mainwin = mainwindow_get_mainwindow();
- RSSylThreadCtx *ctx = g_new0(RSSylThreadCtx, 1);
- gchar *template = NULL;
- gboolean defer_modified_check = FALSE;
-#ifdef RSSYL_DEBUG
- gchar *unixtime_str = NULL, *debugfname = NULL;
-#endif /* RSSYL_DEBUG */
-
-#ifdef USE_PTHREAD
- pthread_t pt;
- pthread_attr_t pta;
-#endif
- gchar *msg = NULL, *tmptitle = NULL;
- gchar *content;
- xmlErrorPtr xml_err;
-
- ctx->url = url;
- ctx->ready = FALSE;
- ctx->last_update = last_update;
- ctx->not_modified = FALSE;
- ctx->defer_modified_check = FALSE;
- ctx->ssl_verify_peer = ssl_verify_peer;
-
- *title = NULL;
-
- g_return_val_if_fail(url != NULL, NULL);
-
- debug_print("RSSyl: XML - url is '%s'\n", url);
-
- msg = g_strdup_printf(_("Fetching '%s'..."), url);
- STATUSBAR_PUSH(mainwin, msg );
- g_free(msg);
-
- GTK_EVENTS_FLUSH();
-
-#ifdef USE_PTHREAD
- if (pthread_attr_init(&pta) != 0 ||
- pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE) != 0 ||
- pthread_create(&pt, &pta, rssyl_fetch_feed_threaded,
- (void *)ctx) != 0 ) {
- /* Bummer, couldn't create thread. Continue non-threaded */
- template = rssyl_fetch_feed_threaded(ctx);
- } else {
- /* Thread created, let's wait until it finishes */
- debug_print("RSSyl: waiting for thread to finish\n");
- while( !ctx->ready ) {
- claws_do_idle();
- }
-
- debug_print("RSSyl: thread finished\n");
- pthread_join(pt, (void *)&template);
- }
-#else
- debug_print("RSSyl: no pthreads, run blocking fetch\n");
- template = rssyl_fetch_feed_threaded(ctx);
-#endif
-
- defer_modified_check = ctx->defer_modified_check;
-
- if (error)
- *error = ctx->error;
-
- g_free(ctx);
- STATUSBAR_POP(mainwin);
-
- if( template == NULL ) {
- debug_print("RSSyl: no feed to parse, returning\n");
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_FETCH, url);
- return NULL;
- }
-
- /* Strip ugly \r\n endlines */
-#ifndef G_OS_WIN32
- file_strip_crs((gchar *)template);
-#endif
- debug_print("parsing %s\n", template);
- doc = xmlParseFile(template);
-
- if( doc == NULL ) {
- claws_unlink(template);
- g_free(template);
- xml_err = xmlGetLastError();
- if (xml_err)
- debug_print("error %s\n", xml_err->message);
-
- g_warning("Couldn't fetch feed '%s', aborting.\n", url);
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_FETCH, url);
- if (error && !(*error)) {
- *error = g_strdup(_("Malformed feed"));
- }
- return NULL;
- }
-
- node = xmlDocGetRootElement(doc);
- rnode = node;
-
-#ifdef RSSYL_DEBUG
- /* debug mode - get timestamp, add it to returned xmlDoc, and make a copy
- * of the fetched feed file */
- tmptitle = rssyl_feed_title_to_dir(url);
- unixtime_str = g_strdup_printf("%ld", time(NULL) );
- debugfname = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, ".", tmptitle, ".", unixtime_str, NULL);
-
- debug_print("Storing fetched feed in file '%s' for debug purposes.\n",
- debugfname);
- link(template, debugfname);
-
- debug_print("Adding 'fetched' property to root node: %s\n", unixtime_str);
- xmlSetProp(rnode, "fetched", unixtime_str);
- g_free(unixtime_str);
- g_free(debugfname);
- g_free(tmptitle);
-#endif /* RSSYL_DEBUG */
-
- claws_unlink(template);
- g_free(template);
-
- debug_print("RSSyl: XML - root node is '%s'\n", node->name);
-
- rootnode = g_ascii_strdown(node->name, -1);
-
- if( !xmlStrcmp(rootnode, "rss") ) {
- context = xmlXPathNewContext(doc);
- xpath = g_strconcat("/", node->name, "/channel/title", NULL);
- debug_print("RSSyl: XML - '%s'\n", xpath);
- if( !(result = xmlXPathEvalExpression(xpath, context)) ) {
- debug_print("RSSyl: XML - no result found for '%s'\n", xpath);
- xmlXPathFreeContext(context);
- g_free(rootnode);
- g_free(xpath);
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PARSE, url);
- return NULL;
- }
-
- if( xmlXPathNodeSetIsEmpty(result->nodesetval) ) {
- debug_print("RSSyl: XML - nodeset empty for '%s'\n", xpath);
- g_free(rootnode);
- g_free(xpath);
- xmlXPathFreeObject(result);
- xmlXPathFreeContext(context);
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PARSE, url);
- return NULL;
- }
- g_free(xpath);
-
- xmlXPathFreeContext(context);
- node = result->nodesetval->nodeTab[0];
- xmlXPathFreeObject(result);
- content = xmlNodeGetContent(node);
- debug_print("RSSyl: XML - title is '%s'\n", content );
- *title = g_strdup(content);
- xmlFree(content);
- debug_print("RSSyl: XML - our title is '%s'\n", *title);
-
- /* use the feed's pubDate to determine if it's modified */
- if( defer_modified_check ) {
- time_t pub_date;
-
- context = xmlXPathNewContext(doc);
- node = rnode;
- xpath = g_strconcat("/", node->name, "/channel/pubDate", NULL);
- debug_print("RSSyl: XML - '%s'\n", xpath);
- if( !(result = xmlXPathEvalExpression(xpath, context)) ) {
- debug_print("RSSyl: XML - no result found for '%s'\n", xpath);
- xmlXPathFreeContext(context);
- g_free(rootnode);
- g_free(xpath);
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PARSE, url);
- return NULL;
- }
-
- if( xmlXPathNodeSetIsEmpty(result->nodesetval) ) {
- debug_print("RSSyl: XML - nodeset empty for '%s', using current time\n",
- xpath);
- pub_date = time(NULL);
- } else {
- node = result->nodesetval->nodeTab[0];
- content = xmlNodeGetContent(node);
- pub_date = procheader_date_parse(NULL, content, 0);
- debug_print("RSSyl: XML - pubDate is '%s'\n", content);
- xmlFree(content);
- }
-
- xmlXPathFreeObject(result);
- xmlXPathFreeContext(context);
- g_free(xpath);
-
- /* check date validity and perform postponed modified check */
- if( pub_date > 0 ) {
- gchar *time_str = NULL;
-
- time_str = createRFC822Date(&pub_date);
- debug_print("RSSyl: XML - item date found: %ld (%s)\n",
- (long int) pub_date, time_str ? time_str : "unknown");
- if( !time_str || ( pub_date < last_update && last_update > 0) ) {
- if( !time_str) {
- debug_print("RSSyl: XML - invalid item date\n");
- } else {
- debug_print("RSSyl: XML - no update needed\n");
- }
- g_free(time_str);
- time_str = NULL;
- g_free(rootnode);
- return NULL;
- }
- g_free(time_str);
- time_str = NULL;
- } else {
- debug_print("RSSyl: XML - item date not found\n");
- g_free(rootnode);
- return NULL;
- }
- }
-
- } else if( !xmlStrcmp(rootnode, "rdf") ) {
- node = node->children;
- /* find "channel" */
- while( node && xmlStrcmp(node->name, "channel") )
- node = node->next;
- /* now find "title" */
- for( n = node->children; n; n = n->next ) {
- if( !xmlStrcmp(n->name, "title") ) {
- content = xmlNodeGetContent(n);
- *title = g_strdup(content);
- xmlFree(content);
- debug_print("RSSyl: XML - RDF our title is '%s'\n", *title);
- }
- }
- } else if( !xmlStrcmp(rootnode, "feed") ) {
- /* find "title" */
- for( n = node->children; n; n = n->next ) {
- if( !xmlStrcmp(n->name, "title") ) {
- content = xmlNodeGetContent(n);
- *title = g_strdup(content);
- xmlFree(content);
- debug_print("RSSyl: XML - FEED our title is '%s'\n", *title);
- }
- }
- } else {
- log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_UNKNOWN, url);
- g_free(rootnode);
- return NULL;
- }
-
- g_return_val_if_fail(*title != NULL, NULL);
-
- if (*title[0] == '\0') {
- g_free(*title);
- *title = g_strdup(url);
- subst_for_shellsafe_filename(*title);
- }
-
- tmptitle = rssyl_feed_title_to_dir(*title);
- dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, tmptitle, NULL);
- g_free(tmptitle);
-
- if( !is_dir_exist(dir) ) {
- if( make_dir(dir) < 0 ) {
- g_warning("couldn't create directory %s\n", dir);
- g_free(rootnode);
- g_free(dir);
- return NULL;
- }
- }
-
- g_free(rootnode);
- g_free(dir);
-
- return doc;
-}
-
-typedef struct _RSSyl_HTMLSymbol RSSyl_HTMLSymbol;
-struct _RSSyl_HTMLSymbol
-{
- gchar *const key;
- gchar *const val;
-};
-
-static RSSyl_HTMLSymbol symbol_list[] = {
- { "<", "<" },
- { ">", ">" },
- { "&", "&" },
- { """, "\"" },
- { "‘", "'" },
- { "’", "'" },
- { "“", "\"" },
- { "”", "\"" },
- { " ", " " },
- { "™", "(TM)" },
- { "", "(TM)" },
- { "'", "'" },
- { "…", "..." },
- { "—", "-" },
- { "<cite>", "\"" },
- { "</cite>", "\"" },
- { "<i>", "" },
- { "</i>", "" },
- { "<em>", "" },
- { "</em>", ""},
- { "<b>", "" },
- { "</b>", "" },
- { "<nobr>", "" },
- { "</nobr>", "" },
- { "<wbr>", "" },
- { NULL, NULL },
-};
-
-static gchar *rssyl_replace_html_symbols(const gchar *text)
-{
- gchar *tmp = NULL, *wtext = g_strdup(text);
- gint i;
-
- for( i = 0; symbol_list[i].key != NULL; i++ ) {
- if( g_strstr_len(text, strlen(text), symbol_list[i].key) ) {
- tmp = rssyl_strreplace(wtext, symbol_list[i].key, symbol_list[i].val);
- wtext = g_strdup(tmp);
- g_free(tmp);
- }
- }
-
- return wtext;
-}
-
-gchar *rssyl_format_string(const gchar *str, gboolean replace_html, gboolean replace_returns)
-{
- gchar *res = NULL;
- gchar *tmp = NULL;
-
- g_return_val_if_fail(str != NULL, NULL);
-
- if (replace_html)
- tmp = rssyl_replace_html_symbols(str);
- else
- tmp = g_strdup(str);
-
- res = rssyl_sanitize_string(tmp, replace_returns);
- g_free(tmp);
-
- g_strstrip(res);
-
- return res;
-}
-
-/* this function splits a string into an array of string, by
- * returning an array of pointers to positions of the delimiter
- * in the original string and replacing this delimiter with a
- * NULL. It does not duplicate memory, hence you should only
- * free the array and not its elements, and you should not
- * free the original string before you're done with the array.
- * maybe could be part of the core (utils.c).
- */
-static gchar **strplit_no_copy(gchar *str, char delimiter)
-{
- gchar **array = g_new(gchar *, 1);
- int i = 0;
- gchar *cur = str, *next;
-
- array[i] = cur;
- i++;
- while ((next = strchr(cur, delimiter)) != NULL) {
- *(next) = '\0';
- array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
- array[i] = next + 1;
- cur = next + 1;
- i++;
- }
- array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
- array[i] = NULL;
- return array;
-}
-
-/* rssyl_parse_folder_item_file()
- *
- * Parse the RFC822-formatted feed item given by "path", and returns a
- * pointer to a RSSylFeedItem struct, which contains all required data.
- */
-static RSSylFeedItem *rssyl_parse_folder_item_file(gchar *dir_path, gchar *filename)
-{
- gchar *contents, **lines, **line, **splid;
- GError *error = NULL;
- RSSylFeedItem *fitem;
- gint i = 0;
- gboolean parsing_headers, past_html_tag, past_endhtml_tag;
- gboolean started_author = FALSE, started_subject = FALSE;
- gboolean started_link = FALSE, started_clink = FALSE, started_plink = FALSE;
- gchar *full_path = g_strconcat(dir_path, G_DIR_SEPARATOR_S, filename, NULL);
- debug_print("RSSyl: parsing '%s'\n", full_path);
-
- g_file_get_contents(full_path, &contents, NULL, &error);
-
- if( error ) {
- g_warning("GError: '%s'\n", error->message);
- g_error_free(error);
- error = NULL;
- }
-
- if( contents ) {
- lines = strplit_no_copy(contents, '\n');
- } else {
- g_warning("Badly formatted file found, ignoring: '%s'\n", full_path);
- g_free(contents);
- return NULL;
- }
-
- fitem = g_new0(RSSylFeedItem, 1); /* free that */
- fitem->date = 0;
- fitem->date_published = 0;
- fitem->link = NULL;
- fitem->text = NULL;
- fitem->id = NULL;
- fitem->id_is_permalink = FALSE;
- fitem->realpath = g_strdup(full_path);
-
- g_free(full_path);
-
- parsing_headers = TRUE;
- past_html_tag = FALSE;
- past_endhtml_tag = FALSE;
- while(lines[i] ) {
- if( parsing_headers && lines[i] && !strlen(lines[i]) && fitem->link ) {
- parsing_headers = FALSE;
- debug_print("RSSyl: finished parsing headers\n");
- }
-
- if( parsing_headers ) {
- line = g_strsplit(lines[i], ": ", 2);
- if( line[0] && line[1] && strlen(line[0]) && lines[i][0] != ' ') {
- started_author = FALSE;
- started_subject = FALSE;
- started_link = FALSE;
- started_clink = FALSE;
- started_plink = FALSE;
-
- /* Author */
- if( !strcmp(line[0], "From") ) {
- fitem->author = g_strdup(line[1]);
- debug_print("RSSyl: got author '%s'\n", fitem->author);
- started_author = TRUE;
- }
-
- /* Date */
- if( !strcmp(line[0], "Date") ) {
- fitem->date = procheader_date_parse(NULL, line[1], 0);
- debug_print("RSSyl: got date \n" );
- }
-
- /* Title */
- if( !strcmp(line[0], "Subject") ) {
- fitem->title = g_strdup(line[1]);
- debug_print("RSSyl: got title '%s'\n", fitem->title);
- started_subject = TRUE;
- }
-
- /* Link */
- if( !strcmp(line[0], "X-RSSyl-URL") ) {
- fitem->link = g_strdup(line[1]);
- debug_print("RSSyl: got link '%s'\n", fitem->link);
- started_link = TRUE;
- }
-
- /* ID */
- if( !strcmp(line[0], "Message-ID") ) {
- splid = g_strsplit_set(line[1], "<>", 3);
- if( strlen(splid[1]) != 0 ) {
- fitem->id = g_strdup(splid[1]);
- debug_print("RSSyl: got id '%s'\n", fitem->id);
- }
- g_strfreev(splid);
- }
-
- if( !strcmp(line[0], "X-RSSyl-Comments") ) {
- fitem->comments_link = g_strdup(line[1]);
- debug_print("RSSyl: got clink '%s'\n", fitem->comments_link);
- started_clink = TRUE;
- }
- if( !strcmp(line[0], "X-RSSyl-Parent") ) {
- fitem->parent_link = g_strdup(line[1]);
- debug_print("RSSyl: got plink '%s'\n", fitem->parent_link);
- started_plink = TRUE;
- }
- } else if (lines[i][0] == ' ') {
- gchar *tmp = NULL;
- /* continuation line */
- if (started_author) {
- tmp = g_strdup_printf("%s %s", fitem->author, lines[i]+1);
- g_free(fitem->author);
- fitem->author = tmp;
- debug_print("RSSyl: updated author to '%s'\n", fitem->author);
- } else if (started_subject) {
- tmp = g_strdup_printf("%s %s", fitem->title, lines[i]+1);
- g_free(fitem->title);
- fitem->title = tmp;
- debug_print("RSSyl: updated title to '%s'\n", fitem->title);
- } else if (started_link) {
- tmp = g_strdup_printf("%s%s", fitem->link, lines[i]+1);
- g_free(fitem->link);
- fitem->link = tmp;
- debug_print("RSSyl: updated link to '%s'\n", fitem->link);
- } else if (started_clink) {
- tmp = g_strdup_printf("%s%s", fitem->comments_link, lines[i]+1);
- g_free(fitem->comments_link);
- fitem->comments_link = tmp;
- debug_print("RSSyl: updated comments_link to '%s'\n", fitem->comments_link);
- } else if (started_plink) {
- tmp = g_strdup_printf("%s%s", fitem->parent_link, lines[i]+1);
- g_free(fitem->parent_link);
- fitem->parent_link = tmp;
- debug_print("RSSyl: updated comments_link to '%s'\n", fitem->parent_link);
- }
- }
- g_strfreev(line);
- } else {
- if( !strcmp(lines[i], RSSYL_TEXT_START) ) {
- debug_print("Leading html tag found at line %d\n", i);
- past_html_tag = TRUE;
- i++;
- continue;
- }
- while( past_html_tag && !past_endhtml_tag && lines[i] ) {
- if( !strcmp(lines[i], RSSYL_TEXT_END) ) {
- debug_print("Trailing html tag found at line %d\n", i);
- past_endhtml_tag = TRUE;
- i++;
- continue;
- }
- if( fitem->text != NULL ) {
- gint e_len, n_len;
- e_len = strlen(fitem->text);
- n_len = strlen(lines[i]);
- fitem->text = g_realloc(fitem->text, e_len + n_len + 2);
- *(fitem->text+e_len) = '\n';
- strcpy(fitem->text+e_len+1, lines[i]);
- *(fitem->text+e_len+n_len+1) = '\0';
- } else {
- fitem->text = g_strdup(lines[i]);
- }
- i++;
- }
-
- if( lines[i] == NULL )
- return fitem;
- }
-
- i++;
- }
- g_free(lines);
- g_free(contents);
- return fitem;
-}
-
-/* rssyl_free_feeditem()
- * frees an RSSylFeedItem
- */
-void rssyl_free_feeditem(RSSylFeedItem *item)
-{
- if (!item)
- return;
- g_free(item->title);
- item->title = NULL;
- g_free(item->text);
- item->text = NULL;
- g_free(item->link);
- item->link = NULL;
- g_free(item->id);
- item->id = NULL;
- g_free(item->comments_link);
- item->comments_link = NULL;
- g_free(item->parent_link);
- item->parent_link = NULL;
- g_free(item->author);
- item->author = NULL;
- g_free(item->realpath);
- item->realpath = NULL;
- if( item->media != NULL ) {
- g_free(item->media->url);
- g_free(item->media->type);
- g_free(item->media);
- }
- g_free(item);
-}
-
-static void *rssyl_read_existing_thr(void *arg)
-{
- RSSylParseCtx *ctx = (RSSylParseCtx *)arg;
- RSSylFolderItem *ritem = ctx->ritem;
- FolderItem *item = &ritem->item;
- RSSylFeedItem *fitem = NULL;
- DIR *dp;
- struct dirent *d;
- gint num;
- gchar *path;
-
- debug_print("RSSyl: rssyl_read_existing_thr()\n");
-
- path = folder_item_get_path(item);
- if( !path ) {
- debug_print("RSSyl: read_existing - path is NULL, bailing out\n");
- ctx->ready = TRUE;
- return NULL;
- }
-
- /* create a new GSList, throw away the old one */
- if( ritem->contents != NULL ) {
- GSList *cur;
- for (cur = ritem->contents; cur; cur = cur->next) {
- RSSylFeedItem *olditem = (RSSylFeedItem *)cur->data;
- rssyl_free_feeditem(olditem);
- }
- g_slist_free(ritem->contents); /* leak fix here */
- ritem->contents = NULL;
- }
- ritem->contents = g_slist_alloc();
-
- if( change_dir(path) < 0 ) {
- g_free(path);
- return NULL;
- }
-
- if( (dp = opendir(".")) == NULL ) {
- FILE_OP_ERROR(item->path, "opendir");
- g_free(path);
- return NULL;
- }
-
- while( (d = readdir(dp)) != NULL ) {
- if (claws_is_exiting()) {
- closedir(dp);
- g_free(path);
- debug_print("RSSyl: read_existing is bailing out, app is exiting\n");
- ctx->ready = TRUE;
- return NULL;
- }
- if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
- debug_print("RSSyl: starting to parse '%s'\n", d->d_name);
- if( (fitem = rssyl_parse_folder_item_file(path, d->d_name)) != NULL ) {
- debug_print("Appending '%s'\n", fitem->title);
- ritem->contents = g_slist_prepend(ritem->contents, fitem);
- }
- }
- }
- closedir(dp);
- g_free(path);
-
- ritem->contents = g_slist_reverse(ritem->contents);
-
- ctx->ready = TRUE;
-
- debug_print("RSSyl: rssyl_read_existing_thr() is returning\n");
- return NULL;
-}
-
-/* rssyl_read_existing()
- *
- * Parse all existing folder items, storing their data in memory. Data is
- * later used for checking for duplicate entries.
- * Of course, actual work is done in a separate thread (if available) in
- * rssyl_read_existing_thr().
- */
-void rssyl_read_existing(RSSylFolderItem *ritem)
-{
- RSSylParseCtx *ctx = NULL;
-#ifdef USE_PTHREAD
- pthread_t pt;
-#endif
-
- g_return_if_fail(ritem != NULL);
-
- ctx = g_new0(RSSylParseCtx, 1);
- ctx->ritem = ritem;
- ctx->ready = FALSE;
-
-#ifdef USE_PTHREAD
- if( pthread_create(&pt, PTHREAD_CREATE_JOINABLE, rssyl_read_existing_thr,
- (void *)ctx) != 0 ) {
- /* Couldn't create thread, continue nonthreaded */
- rssyl_read_existing_thr(ctx);
- } else {
- debug_print("RSSyl: waiting for read_existing thread to finish\n");
- while( !ctx->ready ) {
- claws_do_idle();
- }
-
- debug_print("RSSyl: read_existing thread finished\n");
- pthread_join(pt, NULL);
- }
-#else
- debug_print("RSSyl: pthreads not available, running read_existing non-threaded\n");
- rssyl_read_existing_thr(ctx);
-#endif
-
- g_free(ctx);
-}
-
-/* rssyl_cb_feed_compare()
- *
- * Callback compare function called by glib2's g_slist_find_custom().
- */
-static gint rssyl_cb_feed_compare(const RSSylFeedItem *a,
- const RSSylFeedItem *b)
-{
- gboolean date_publ_eq = FALSE, date_eq = FALSE;
- gboolean link_eq = FALSE, title_eq = FALSE;
- gboolean no_link = FALSE, no_title = FALSE;
- gchar *atit = NULL, *btit = NULL;
-
- if( a == NULL || b == NULL )
- return 1;
-
- /* ID should be unique. If it matches, we've found what we came for. */
- if( (a->id != NULL) && (b->id != NULL) ) {
- if( strcmp(a->id, b->id) == 0 )
- return 0;
-
- /* If both IDs are present, but they do not match, we need
- * to look elsewhere. */
- return 1;
- }
-
- /* Ok, we have no ID to aid us. Let's have a look at item timestamps,
- * item link and title. */
- if( (a->link != NULL) && (b->link != NULL) ) {
- if( strcmp(a->link, b->link) == 0 )
- link_eq = TRUE;
- } else
- no_link = TRUE;
-
- if( (a->title != NULL) && (b->title != NULL) ) {
- atit = conv_unmime_header(a->title, CS_UTF_8, FALSE);
- btit = conv_unmime_header(b->title, CS_UTF_8, FALSE);
- if( strcmp(atit, btit) == 0 )
- title_eq = TRUE;
- g_free(atit);
- g_free(btit);
- } else
- no_title = TRUE;
-
- /* If there's no 'published' timestamp for the item, we can only judge
- * by item link and title - 'modified' timestamp can have changed if the
- * item was updated recently. */
- if( b->date_published <= 0 && b->date <= 0) {
- if( link_eq && (title_eq || no_title) )
- return 0;
- }
-
- if( ((a->date_published > 0) && (b->date_published > 0) &&
- (a->date_published == b->date_published)) ) {
- date_publ_eq = TRUE;
- }
-
- if( ((a->date > 0) && (b->date > 0) &&
- (a->date == b->date)) ) {
- date_eq = TRUE;
- }
-
- /* If 'published' time and item link match, it is reasonable to assume
- * it's this item. */
- if( (link_eq || no_link) && (date_publ_eq || date_eq) )
- return 0;
-
- /* Last ditch effort - if everything else is missing, at least titles
- * should match. */
- if( no_link && title_eq )
- return 0;
-
- /* We don't know this item. */
- return 1;
-}
-
-enum {
- ITEM_UNCHANGED,
- ITEM_CHANGED_TEXTONLY,
- ITEM_CHANGED
-};
-
-static gint rssyl_feed_item_changed(RSSylFeedItem *old_item, RSSylFeedItem *new_item)
-{
- /* if both have title ... */
- if( old_item->title && new_item->title ) {
- gchar *old = conv_unmime_header(old_item->title, CS_UTF_8, FALSE);
- gchar *new = conv_unmime_header(new_item->title, CS_UTF_8, FALSE);
- if( strcmp(old, new) ) { /* ... compare "unmimed" titles */
- g_free(old);
- g_free(new);
- debug_print("RSSyl: item titles differ\n");
- return ITEM_CHANGED;
- }
- g_free(old);
- g_free(new);
- } else {
- /* if atleast one has a title, they differ */
- if( old_item->title || new_item->title ) {
- debug_print("RSSyl: +/- title\n");
- return ITEM_CHANGED;
- }
- }
-
- /* if both have author ... */
- if( old_item->author && new_item->author ) {
- gchar *old = conv_unmime_header(old_item->author, CS_UTF_8, TRUE);
- gchar *new = conv_unmime_header(new_item->author, CS_UTF_8, TRUE);
- if( strcmp(old, new) ) { /* ... compare "unmimed" authors */
- g_free(old);
- g_free(new);
- debug_print("RSSyl: item authors differ\n");
- return ITEM_CHANGED;
- }
- g_free(old);
- g_free(new);
- } else {
- /* if atleast one has author, they differ */
- if( old_item->author || new_item->author ) {
- debug_print("RSSyl: +/- author\n");
- return ITEM_CHANGED;
- }
- }
-
- /* if both have text ... */
- if( old_item->text && new_item->text ) {
- if( strcmp(old_item->text, new_item->text) ) { /* ... compare texts */
- debug_print("RSSyl: item texts differ\n");
- return ITEM_CHANGED_TEXTONLY;
- }
- } else {
- /* if atleast one has some text, they differ */
- if( old_item->text || new_item->text ) {
- debug_print("RSSyl: +/- text\n");
- if ( !old_item->text )
- debug_print("RSSyl: old_item has no text\n");
- else
- debug_print("RSSyl: new_item has no text\n");
- return ITEM_CHANGED_TEXTONLY;
- }
- }
-
- /* they don't seem to differ */
- return ITEM_UNCHANGED;
-}
-
-/* rssyl_feed_item_exists()
- *
- * Returns 1 if a feed item already exists locally, 2 if there's a changed
- * item with link that already belongs to existing item, 3 if only item's
- * text has changed, 0 if item is new.
- */
-enum {
- EXISTS_NEW,
- EXISTS_UNCHANGED,
- EXISTS_CHANGED,
- EXISTS_CHANGED_TEXTONLY
-};
-
-static guint rssyl_feed_item_exists(RSSylFolderItem *ritem,
- RSSylFeedItem *fitem, RSSylFeedItem **oldfitem)
-{
- gint changed;
- GSList *item = NULL;
- RSSylFeedItem *efitem = NULL;
- g_return_val_if_fail(ritem != NULL, FALSE);
- g_return_val_if_fail(fitem != NULL, FALSE);
-
- if( ritem->contents == NULL || g_slist_length(ritem->contents) == 0 )
- return 0;
-
- if( (item = g_slist_find_custom(ritem->contents,
- (gconstpointer)fitem, (GCompareFunc)rssyl_cb_feed_compare)) ) {
- efitem = (RSSylFeedItem *)item->data;
- if( (changed = rssyl_feed_item_changed(efitem, fitem)) > EXISTS_NEW ) {
- *oldfitem = efitem;
- if (changed == ITEM_CHANGED_TEXTONLY)
- return EXISTS_CHANGED_TEXTONLY;
- else
- return EXISTS_CHANGED;
- }
- return EXISTS_UNCHANGED;
- }
-
- return EXISTS_NEW;
-}
-
-void rssyl_parse_feed(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
-{
- xmlNodePtr node;
- gchar *rootnode;
- MainWindow *mainwin = mainwindow_get_mainwindow();
- gint count;
- gchar *msg;
-
- if (doc == NULL)
- return;
-
- rssyl_read_existing(ritem);
-
- if (claws_is_exiting()) {
- debug_print("RSSyl: parse_feed bailing out, app is exiting\n");
- return;
- }
-
- node = xmlDocGetRootElement(doc);
-
- debug_print("RSSyl: XML - root node is '%s'\n", node->name);
- rootnode = g_ascii_strdown(node->name, -1);
-
- msg = g_strdup_printf(_("Refreshing feed '%s'..."),
- ritem->item.name);
- STATUSBAR_PUSH(mainwin, msg );
- g_free(msg);
- GTK_EVENTS_FLUSH();
-
- folder_item_update_freeze();
-
- /* we decide what parser to call, depending on what the root node is */
- if( !strcmp(rootnode, "rss") ) {
- debug_print("RSSyl: XML - calling rssyl_parse_rss()\n");
- count = rssyl_parse_rss(doc, ritem, parent);
- } else if( !strcmp(rootnode, "rdf") ) {
- debug_print("RSSyl: XML - calling rssyl_parse_rdf()\n");
- if (ritem->fetch_comments) {
- log_error(LOG_PROTOCOL, _("RSSyl: Fetching comments is not supported for RDF feeds. "
- "Cannot fetch comments of '%s'"), ritem->item.name);
- ritem->fetch_comments = FALSE;
- }
- count = rssyl_parse_rdf(doc, ritem, parent);
- } else if( !strcmp(rootnode, "feed") ) {
- debug_print("RSSyl: XML - calling rssyl_parse_atom()\n");
- count = rssyl_parse_atom(doc, ritem, parent);
- } else {
- alertpanel_error(_("This feed format is not supported yet."));
- count = 0;
- }
-
- if (!parent)
- ritem->last_count = count;
-
- folder_item_scan(&ritem->item);
- folder_item_update_thaw();
-
- STATUSBAR_POP(mainwin);
-
- g_free(rootnode);
-}
-
-gboolean rssyl_add_feed_item(RSSylFolderItem *ritem, RSSylFeedItem *fitem)
-{
- MsgFlags *flags;
- gchar *template, *tmpurl, *tmpid;
- gchar tmp[10240];
- gint d = -1, fd, dif = 0;
- FILE *f;
- RSSylFeedItem *oldfitem = NULL;
- gchar *meta_charset = NULL, *url_html = NULL;
- gboolean err = FALSE;
-
- g_return_val_if_fail(ritem != NULL, FALSE);
- g_return_val_if_fail(ritem->item.path != NULL, FALSE);
- g_return_val_if_fail(fitem != NULL, FALSE);
-
- if( !fitem->author )
- fitem->author = g_strdup(_("N/A"));
-
- /* Skip if the item already exists */
- dif = rssyl_feed_item_exists(ritem, fitem, &oldfitem);
- if( dif == 1 ) {
- debug_print("RSSyl: This item already exists, skipping...\n");
- return FALSE;
- }
- if( dif >= 2 && oldfitem != NULL ) {
- debug_print("RSSyl: Item changed, removing old one and adding new...\n");
- ritem->contents = g_slist_remove(ritem->contents, oldfitem);
- g_remove(oldfitem->realpath);
- rssyl_free_feeditem(oldfitem);
- oldfitem = NULL;
- }
-
- /* Adjust some fields */
- if( fitem->date <= 0 )
- fitem->date = time(NULL);
-
- debug_print("RSSyl: Adding item '%s' (%d)\n", fitem->title, dif);
-
- ritem->contents = g_slist_append(ritem->contents, fitem);
-
- flags = g_new(MsgFlags, 1);
-#ifndef G_OS_WIN32
- template = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, RSSYL_TMP_TEMPLATE, NULL);
- fd = mkstemp(template);
-#else
- template = get_tmp_file();
- fd = open(template, (O_CREAT|O_RDWR|O_BINARY), (S_IRUSR|S_IWUSR));
-#endif
-
- f = fdopen(fd, "w");
- g_return_val_if_fail(f != NULL, FALSE);
-
- if( fitem->date != 0 ) {
- gchar *tmpdate = createRFC822Date(&fitem->date);
- err |= (fprintf(f, "Date: %s\n", tmpdate ) < 0);
- g_free(tmpdate);
- }
-
- if( fitem->author ) {
- if (g_utf8_validate(fitem->author, -1, NULL)) {
- conv_encode_header_full(tmp, 10239, fitem->author,
- strlen("From: "), TRUE, CS_UTF_8);
- err |= (fprintf(f, "From: %s\n", tmp) < 0);
- } else
- err |= (fprintf(f, "From: %s\n", fitem->author) < 0);
- }
-
- if( fitem->title ) {
- if (g_utf8_validate(fitem->title, -1, NULL)) {
- conv_encode_header_full(tmp, 10239, fitem->title,
- strlen("Subject: "), FALSE, CS_UTF_8);
- err |= (fprintf(f, "Subject: %s\n", tmp) < 0);
- } else
- err |= (fprintf(f, "Subject: %s\n", fitem->title) < 0);
- }
-
- if( (tmpurl = fitem->link) == NULL ) {
- if( fitem->id != NULL && fitem->id_is_permalink )
- tmpurl = fitem->id;
- }
- if( tmpurl != NULL )
- err |= (fprintf(f, "X-RSSyl-URL: %s\n", tmpurl) < 0);
-
- if( (tmpid = fitem->id) == NULL )
- tmpid = fitem->link;
- if( tmpid != NULL )
- err |= (fprintf(f, "Message-ID: <%s>\n", tmpid) < 0);
-
- if( fitem->comments_link ) {
- err |= (fprintf(f, "X-RSSyl-Comments: %s\n", fitem->comments_link) < 0);
- }
- if( fitem->parent_link) {
- err |= (fprintf(f, "X-RSSyl-Parent: %s\n", fitem->parent_link) < 0);
- err |= (fprintf(f, "References: <%s>\n", fitem->parent_link) < 0);
- }
-
-#ifdef RSSYL_DEBUG
- if( fitem->debug_fetched != -1 ) {
- err |= (fprintf(f, "X-RSSyl-Debug-Fetched: %ld\n", fitem->debug_fetched) < 0);
- }
-#endif /* RSSYL_DEBUG */
-
- if (fitem->text && g_utf8_validate(fitem->text, -1, NULL)) {
- /* if it passes UTF-8 validation, specify it. */
- err |= (fprintf(f, "Content-Type: text/html; charset=UTF-8\n\n") < 0);
- meta_charset = g_strdup("<meta http-equiv=\"Content-Type\" "
- "content=\"text/html; charset=UTF-8\">");
- } else {
- /* make sure Claws Mail displays it as html */
- err |= (fprintf(f, "Content-Type: text/html\n\n") < 0);
- }
-
- if( tmpurl )
- url_html = g_strdup_printf("<p>URL: <a href=\"%s\">%s</a></p>\n<br>\n",
- tmpurl, tmpurl);
-
- err |= (fprintf(f, "<html><head>"
- "%s\n"
- "<base href=\"%s\">\n"
- "</head><body>\n"
- "%s"
- RSSYL_TEXT_START"\n"
- "%s%s"
- RSSYL_TEXT_END"\n\n",
-
- meta_charset ? meta_charset:"",
- fitem->link,
- url_html?url_html:"",
- (fitem->text ? fitem->text : ""),
- (fitem->text ? "\n" : "") ) < 0 );
-
- g_free(meta_charset);
- g_free(url_html);
- if( fitem->media ) {
- if( fitem->media->size > 0 ) {
- tmpid = g_strdup_printf(ngettext("%ld byte", "%ld bytes",
- fitem->media->size), fitem->media->size);
- } else {
- tmpid = g_strdup(_("size unknown"));
- }
-
- fprintf(f, "<p><a href=\"%s\">Attached media file</a> [%s] (%s)</p>\n",
- fitem->media->url, fitem->media->type, tmpid);
-
- g_free(tmpid);
- }
-
- if( fitem->media )
- err |= (fprintf(f,
- "<p><a href=\"%s\">Attached media file</a> [%s] (%ld bytes)</p>\n",
- fitem->media->url, fitem->media->type, fitem->media->size) < 0);
-
- err |= (fprintf(f, "</body></html>\n") < 0);
-
- err |= (fclose(f) == EOF);
-
- if (!err) {
- g_return_val_if_fail(template != NULL, FALSE);
- d = folder_item_add_msg(&ritem->item, template, flags, TRUE);
- }
- g_free(template);
-
- if (ritem->silent_update == 2
- || (ritem->silent_update == 1 && dif == EXISTS_CHANGED_TEXTONLY))
- procmsg_msginfo_unset_flags(
- folder_item_get_msginfo((FolderItem *)ritem, d), MSG_NEW | MSG_UNREAD, 0);
- else
-
- debug_print("RSSyl: folder_item_add_msg(): %d, err %d\n", d, err);
-
- return err ? FALSE:TRUE;
-}
-
-MsgInfo *rssyl_parse_feed_item_to_msginfo(gchar *file, MsgFlags flags,
- gboolean a, gboolean b, FolderItem *item)
-{
- MsgInfo *msginfo;
-
- g_return_val_if_fail(item != NULL, NULL);
-
- msginfo = procheader_parse_file(file, flags, a, b);
- if (msginfo)
- msginfo->folder = item;
-
- return msginfo;
-}
-
-void rssyl_remove_feed_cache(FolderItem *item)
-{
- gchar *path;
- DIR *dp;
- struct dirent *d;
- gint num = 0;
-
- g_return_if_fail(item != NULL);
-
- debug_print("Removing local cache for '%s'\n", item->name);
-
- path = folder_item_get_path(item);
- g_return_if_fail(path != NULL);
- if( change_dir(path) < 0 ) {
- g_free(path);
- return;
- }
-
- debug_print("Emptying '%s'\n", path);
-
- if( (dp = opendir(".")) == NULL ) {
- FILE_OP_ERROR(item->path, "opendir");
- return;
- }
-
- while( (d = readdir(dp)) != NULL ) {
- g_remove(d->d_name);
- num++;
- }
- closedir(dp);
-
- debug_print("Removed %d files\n", num);
-
- g_remove(path);
- g_free(path);
-}
-
-void rssyl_update_comments(RSSylFolderItem *ritem)
-{
- FolderItem *item = &ritem->item;
- RSSylFeedItem *fitem = NULL;
- DIR *dp;
- struct dirent *d;
- gint num;
- gchar *path;
-
- g_return_if_fail(ritem != NULL);
-
- if (ritem->fetch_comments == FALSE)
- return;
-
- path = folder_item_get_path(item);
- g_return_if_fail(path != NULL);
- if( change_dir(path) < 0 ) {
- g_free(path);
- return;
- }
-
- if( (dp = opendir(".")) == NULL ) {
- FILE_OP_ERROR(item->path, "opendir");
- g_free(path);
- return;
- }
-
- while( (d = readdir(dp)) != NULL ) {
- if (claws_is_exiting()) {
- g_free(path);
- closedir(dp);
- debug_print("RSSyl: update_comments bailing out, app is exiting\n");
- return;
- }
-
- if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
- debug_print("RSSyl: starting to parse '%s'\n", d->d_name);
- if( (fitem = rssyl_parse_folder_item_file(path, d->d_name)) != NULL ) {
- xmlDocPtr doc;
- gchar *title;
- if (fitem->comments_link && fitem->id &&
- (ritem->fetch_comments_for == -1 ||
- time(NULL) - fitem->date <= ritem->fetch_comments_for*86400)) {
- debug_print("RSSyl: fetching comments '%s'\n", fitem->comments_link);
- doc = rssyl_fetch_feed(fitem->comments_link, ritem->item.mtime,
- ritem->ssl_verify_peer, &title, NULL);
- rssyl_parse_feed(doc, ritem, fitem->id);
- xmlFreeDoc(doc);
- g_free(title);
- }
- rssyl_free_feeditem(fitem);
- }
- }
- }
- closedir(dp);
- g_free(path);
-
- debug_print("RSSyl: rssyl_update_comments() is returning\n");
-}
-
-void rssyl_update_feed(RSSylFolderItem *ritem)
-{
- gchar *title = NULL, *dir = NULL, *error = NULL, *dir2, *tmp;
- xmlDocPtr doc = NULL;
-
- g_return_if_fail(ritem != NULL);
-
- if( !ritem->url )
- rssyl_get_feed_props(ritem);
- g_return_if_fail(ritem->url != NULL);
-
- log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATING, ritem->url);
-
- doc = rssyl_fetch_feed(ritem->url, ritem->item.mtime, ritem->ssl_verify_peer, &title, &error);
-
- if (claws_is_exiting()) {
- debug_print("RSSyl: Claws-Mail is exiting, aborting feed parsing\n");
- log_print(LOG_PROTOCOL, RSSYL_LOG_EXITING);
- if (error)
- g_free(error);
- if (doc)
- xmlFreeDoc(doc);
- g_free(title);
- g_free(dir);
- return;
- }
-
- if (error) {
- log_error(LOG_PROTOCOL, _("RSSyl: Cannot update feed %s:\n%s\n"), ritem->url, error);
- }
- g_free(error);
-
- if (doc && title) {
- tmp = rssyl_feed_title_to_dir(title);
- dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, tmp, NULL);
- g_free(tmp);
- if( strcmp(title, ritem->official_name) ) {
- tmp = rssyl_feed_title_to_dir((&ritem->item)->name);
- dir2 = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
- G_DIR_SEPARATOR_S, tmp,
- NULL);
- g_free(tmp);
- if( g_rename(dir2, dir) == -1 ) {
- g_warning("couldn't rename directory '%s'\n", dir2);
- g_free(dir);
- g_free(dir2);
- g_free(title);
- xmlFreeDoc(doc);
- return;
- }
- g_free(dir2);
-
- rssyl_props_update_name(ritem, title);
-
- g_free((&ritem->item)->name);
- (&ritem->item)->name = g_strdup(title);
- g_free(ritem->official_name);
- ritem->official_name = g_strdup(title);
- folder_item_rename(&ritem->item, title);
- rssyl_store_feed_props(ritem);
- }
-
- rssyl_parse_feed(doc, ritem, NULL);
-
- if (claws_is_exiting()) {
- debug_print("RSSyl: Claws-Mail is exiting, aborting feed parsing\n");
- log_print(LOG_PROTOCOL, RSSYL_LOG_EXITING);
- if (error)
- g_free(error);
- if (doc)
- xmlFreeDoc(doc);
- g_free(title);
- g_free(dir);
- return;
- }
-
- rssyl_expire_items(ritem);
- }
-
- if (claws_is_exiting()) {
- g_free(title);
- g_free(dir);
- if (doc)
- xmlFreeDoc(doc);
- return;
- }
-
- if( ritem->fetch_comments == TRUE)
- rssyl_update_comments(ritem);
-
- ritem->item.mtime = time(NULL);
- debug_print("setting %s mtime to %ld\n", ritem->item.name, (long int)time(NULL));
-
- if (doc)
- xmlFreeDoc(doc);
- g_free(title);
- g_free(dir);
-
- log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATED, ritem->url);
-}
-
-void rssyl_start_refresh_timeout(RSSylFolderItem *ritem)
-{
- RSSylRefreshCtx *ctx;
- guint source_id;
- RSSylPrefs *rsprefs = NULL;
-
- g_return_if_fail(ritem != NULL);
-
- if( ritem->default_refresh_interval ) {
- rsprefs = rssyl_prefs_get();
- ritem->refresh_interval = rsprefs->refresh;
- }
-
- /* Do not start refreshing if the interval is set to 0 */
- if( ritem->refresh_interval == 0 )
- return;
-
- ctx = g_new0(RSSylRefreshCtx, 1);
- ctx->ritem = ritem;
-
- source_id = g_timeout_add(ritem->refresh_interval * 60 * 1000,
- (GSourceFunc)rssyl_refresh_timeout_cb, ctx );
- ritem->refresh_id = source_id;
- ctx->id = source_id;
-
- debug_print("RSSyl: start_refresh_timeout - %d min (id %d)\n",
- ritem->refresh_interval, ctx->id);
-}
-
-static void rssyl_find_feed_by_url_func(FolderItem *item, gpointer data)
-{
- RSSylFolderItem *ritem;
- RSSylFindByUrlCtx *ctx = (RSSylFindByUrlCtx *)data;
-
- /* If we've already found a feed with desired URL, don't do anything */
- if( ctx->ritem != NULL )
- return;
-
- /* Only check rssyl folders */
- if( !IS_RSSYL_FOLDER_ITEM(item) )
- return;
-
- /* Don't bother with the root folder */
- if( folder_item_parent(item) == NULL )
- return;
-
- ritem = (RSSylFolderItem *)item;
-
- if( ritem->url && ctx->url && !strcmp(ritem->url, ctx->url) )
- ctx->ritem = ritem;
-}
-
-static RSSylFolderItem *rssyl_find_feed_by_url(gchar *url)
-{
- RSSylFindByUrlCtx *ctx = NULL;
- RSSylFolderItem *ritem = NULL;
-
- g_return_val_if_fail(url != NULL, NULL);
-
- ctx = g_new0(RSSylFindByUrlCtx, 1);
- ctx->url = url;
- ctx->ritem = NULL;
-
- folder_func_to_all_folders(
- (FolderItemFunc)rssyl_find_feed_by_url_func, ctx);
-
- if( ctx->ritem != NULL )
- ritem = ctx->ritem;
-
- g_free(ctx);
-
- return ritem;
-}
-
-FolderItem *rssyl_subscribe_new_feed(FolderItem *parent, const gchar *url,
- gboolean verbose)
-{
- gchar *title = NULL;
- xmlDocPtr doc;
- FolderItem *new_item;
- RSSylFolderItem *ritem;
- gchar *myurl = NULL;
- gchar *error = NULL;
-
- g_return_val_if_fail(parent != NULL, NULL);
- g_return_val_if_fail(url != NULL, NULL);
-
- if (!strncmp(url, "feed://", 7))
- myurl = g_strdup(url+7);
- else if (!strncmp(url, "feed:", 5))
- myurl = g_strdup(url+5);
- else
- myurl = g_strdup(url);
-
- myurl = g_strchomp(myurl);
-
- if( rssyl_find_feed_by_url(myurl) != NULL ) {
- if (verbose)
- alertpanel_error(_("You are already subscribed to this feed."));
- g_free(myurl);
- return NULL;
- }
-
- main_window_cursor_wait(mainwindow_get_mainwindow());
- doc = rssyl_fetch_feed(myurl, -1, rssyl_prefs_get()->ssl_verify_peer, &title, &error);
- main_window_cursor_normal(mainwindow_get_mainwindow());
- if( !doc || !title ) {
- if (verbose) {
- gchar *tmp;
- tmp = g_markup_printf_escaped(_("Couldn't fetch URL '%s':\n%s"),
- myurl, error ? error:_("Unknown error"));
- alertpanel_error("%s", tmp);
- g_free(tmp);
- } else
- log_error(LOG_PROTOCOL, _("Couldn't fetch URL '%s':\n%s\n"), myurl, error ? error:_("Unknown error"));
- g_free(myurl);
- g_free(error);
- g_free(title);
- if (doc)
- xmlFreeDoc(doc);
- return NULL;
- }
-
- g_free(error);
-
- new_item = folder_create_folder(parent, title);
- if( !new_item ) {
- if (verbose) {
- gchar *tmp;
- tmp = g_markup_printf_escaped("%s", title);
- alertpanel_error(_("Can't subscribe feed '%s'."), title);
- g_free(tmp);
- }
- g_free(myurl);
- xmlFreeDoc(doc);
- return NULL;
- }
-
- ritem = (RSSylFolderItem *)new_item;
- ritem->url = myurl;
-
- ritem->default_refresh_interval = TRUE;
- ritem->default_expired_num = TRUE;
-
- rssyl_store_feed_props(ritem);
-
- folder_write_list();
-
- rssyl_parse_feed(doc, ritem, NULL);
- xmlFreeDoc(doc);
-
- rssyl_expire_items(ritem);
-
- /* TODO: allow user to enable this when adding the feed */
- if( ritem->fetch_comments )
- rssyl_update_comments(ritem);
-
- /* update official_title */
- rssyl_store_feed_props(ritem);
- rssyl_start_refresh_timeout(ritem);
-
- folder_item_scan(new_item);
- return new_item;
-}
-
-static gint rssyl_expire_sort_func(RSSylFeedItem *a, RSSylFeedItem *b)
-{
- if( !a || !b )
- return 0;
-
- return (b->date - a->date);
-}
-
-void rssyl_expire_items(RSSylFolderItem *ritem)
-{
- int num;
- RSSylFeedItem *fitem;
- GSList *i;
-
- g_return_if_fail(ritem != NULL);
-
- rssyl_read_existing(ritem);
-
- g_return_if_fail(ritem->contents != NULL);
-
- num = ritem->expired_num;
- if( num == -1 ||
- (num > (g_slist_length(ritem->contents) - ritem->last_count)) )
- return;
-
- debug_print("RSSyl: rssyl_expire_items()\n");
-
- ritem->contents = g_slist_sort(ritem->contents,
- (GCompareFunc)rssyl_expire_sort_func);
-
- debug_print("RSSyl: finished sorting\n");
-
- while( (i = g_slist_nth(ritem->contents, ritem->last_count + num + 1)) ) {
- fitem = (RSSylFeedItem *)i->data;
- debug_print("RSSyl: expiring '%s'\n", fitem->realpath);
- g_remove(fitem->realpath);
- rssyl_free_feeditem(fitem);
- fitem = NULL;
- ritem->contents = g_slist_remove(ritem->contents, i->data);
- }
-
- folder_item_scan(&ritem->item);
-
- debug_print("RSSyl: finished expiring\n");
-}
-
-
-void rssyl_refresh_all_func(FolderItem *item, gpointer data)
-{
- RSSylFolderItem *ritem = (RSSylFolderItem *)item;
- /* Only try to refresh our feed folders */
- if( !IS_RSSYL_FOLDER_ITEM(item) )
- return;
-
- /* Don't try to refresh the root folder */
- if( folder_item_parent(item) == NULL )
- 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);
-}
diff --git a/src/plugins/rssyl/feed.h b/src/plugins/rssyl/feed.h
deleted file mode 100644
index 1d66b7d..0000000
--- a/src/plugins/rssyl/feed.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#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, gboolean ssl_verify_peer, 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 */
diff --git a/src/plugins/rssyl/feedprops.c b/src/plugins/rssyl/feedprops.c
deleted file mode 100644
index d52bde5..0000000
--- a/src/plugins/rssyl/feedprops.c
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * 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);
-
- t_prop = g_strdup_printf("%d", ritem->ssl_verify_peer);
- xmlSetProp(node, RSSYL_PROP_SSL_VERIFY_PEER, 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;
-
- /* ssl_verify_peer */
- tmp = xmlGetProp(node, RSSYL_PROP_SSL_VERIFY_PEER);
- tmpi = 0;
- if( tmp ) {
- tmpi = atoi(tmp);
- ritem->ssl_verify_peer = 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);
-}
diff --git a/src/plugins/rssyl/feedprops.h b/src/plugins/rssyl/feedprops.h
deleted file mode 100644
index f4a3b27..0000000
--- a/src/plugins/rssyl/feedprops.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#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"
-#define RSSYL_PROP_SSL_VERIFY_PEER "ssl_verify_peer"
-
-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 */
diff --git a/src/plugins/rssyl/libfeed/Makefile.am b/src/plugins/rssyl/libfeed/Makefile.am
new file mode 100644
index 0000000..227b25e
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/Makefile.am
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libfeed.la
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I.
+
+libfeed_la_CPPFLAGS = \
+ -Wall \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
+libfeed_la_SOURCES = \
+ date.c date.h \
+ feed.c feed.h \
+ feeditem.c feeditem.h \
+ feeditemenclosure.c feeditemenclosure.h \
+ parser.c parser.h \
+ parser_atom10.c parser_atom10.h \
+ parser_opml.c parser_opml.h \
+ parser_rdf.c parser_rdf.h \
+ parser_rss20.c parser_rss20.h
diff --git a/src/plugins/rssyl/date.c b/src/plugins/rssyl/libfeed/date.c
similarity index 76%
rename from src/plugins/rssyl/date.c
rename to src/plugins/rssyl/libfeed/date.c
index 4145f52..2b8ffff 100644
--- a/src/plugins/rssyl/date.c
+++ b/src/plugins/rssyl/libfeed/date.c
@@ -33,15 +33,7 @@
#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
+#define _XOPEN_SOURCE /* glibc2 needs this */
#include <time.h>
#include <glib.h>
@@ -50,46 +42,17 @@
#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
@@ -97,7 +60,7 @@ time_t parseISO8601Date(gchar *date) {
*/
/* full specified variant */
- if(NULL != (pos = strptime((const char *)date, "%t%Y-%m-%dT%H:%M%t", &tm))) {
+ if(NULL != (pos = strptime((const char *)date, "%Y-%m-%dT%H:%M:%SZ", &tm))) {
/* Parse seconds */
if (*pos == ':')
pos++;
@@ -127,13 +90,12 @@ time_t parseISO8601Date(gchar *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));
+ t2 = mktime(gmtime(&t));
t = t - (t2 - t);
return t;
@@ -152,16 +114,34 @@ gchar *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"
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 */
- }
+
+ tm = gmtime(time); /* 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);
}
+
+time_t parseRFC822Date(gchar *date)
+{
+ struct tm t;
+ memset(&t, 0, sizeof(struct tm));
+ const char *c = setlocale(LC_TIME, NULL);
+
+ /* Adjust the LC_TIME locale to standard C in order for strptime()
+ * to work reliably. */
+ if (c != NULL)
+ setlocale(LC_TIME, "C");
+
+ if (!strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) &&
+ !strptime(date, "%a, %d %b %Y %H:%M %Z", &t)) {
+ g_warning("Invalid RFC822 date!\n");
+ if (c != NULL)
+ setlocale(LC_TIME, c);
+ return 0;
+ }
+
+ /* Restore the original LC_TIME locale. */
+ if (c != NULL)
+ setlocale(LC_TIME, c);
+
+ return mktime(&t);
+}
diff --git a/src/plugins/rssyl/date.h b/src/plugins/rssyl/libfeed/date.h
similarity index 82%
rename from src/plugins/rssyl/date.h
rename to src/plugins/rssyl/libfeed/date.h
index 27c1353..c8ea1a6 100644
--- a/src/plugins/rssyl/date.h
+++ b/src/plugins/rssyl/libfeed/date.h
@@ -6,5 +6,6 @@
time_t parseISO8601Date(gchar *date);
gchar *createRFC822Date(const time_t *time);
+time_t parseRFC822Date(gchar *date);
#endif /* __DATE_H */
diff --git a/src/plugins/rssyl/libfeed/feed.c b/src/plugins/rssyl/libfeed/feed.c
new file mode 100644
index 0000000..7719278
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feed.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#define __USE_GNU
+
+#include <stdlib.h>
+#include <glib.h>
+#include <curl/curl.h>
+#include <expat.h>
+
+#include "feed.h"
+#include "parser.h"
+
+/* feed_new()
+ * Initializes new Feed struct, setting its url and a default timeout. */
+Feed *feed_new(gchar *url)
+{
+ Feed *feed = NULL;
+
+ g_return_val_if_fail(url != NULL, NULL);
+
+ feed = malloc( sizeof(Feed) );
+ g_return_val_if_fail(feed != NULL, NULL);
+
+ feed->timeout = FEED_DEFAULT_TIMEOUT;
+ feed->url = g_strdup(url);
+ feed->title = NULL;
+ feed->description = NULL;
+ feed->language = NULL;
+ feed->author = NULL;
+ feed->generator = NULL;
+ feed->items = NULL;
+
+ feed->fetcherr = NULL;
+ feed->cookies_path = NULL;
+
+ return feed;
+}
+
+static void _free_items(gpointer item, gpointer nada)
+{
+ feed_item_free(item);
+}
+
+void feed_free(Feed *feed)
+{
+ if( feed == NULL )
+ return; /* Return silently, without printing a glib error. */
+
+ g_free(feed->url);
+ g_free(feed->title);
+ g_free(feed->description);
+ g_free(feed->language);
+ g_free(feed->author);
+ g_free(feed->generator);
+ g_free(feed->fetcherr);
+ g_free(feed->cookies_path);
+
+ if( feed->items != NULL ) {
+ g_slist_foreach(feed->items, _free_items, NULL);
+ g_slist_free(feed->items);
+ }
+
+ g_free(feed);
+ feed = NULL;
+}
+
+void feed_free_items(Feed *feed)
+{
+ if( feed == NULL )
+ return;
+
+ if( feed->items != NULL ) {
+ g_slist_foreach(feed->items, _free_items, NULL);
+ g_slist_free(feed->items);
+ feed->items = NULL;
+ }
+}
+
+/* Timeout */
+void feed_set_timeout(Feed *feed, guint timeout)
+{
+ g_return_if_fail(feed != NULL);
+ feed->timeout = timeout;
+}
+
+guint feed_get_timeout(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, 0);
+ return feed->timeout;
+}
+
+/* URL */
+void feed_set_url(Feed *feed, gchar *url)
+{
+ g_return_if_fail(feed != NULL);
+ g_return_if_fail(url != NULL);
+
+ if( feed->url != NULL ) {
+ g_free(feed->url);
+ feed->url = NULL;
+ }
+
+ feed->url = g_strdup(url);
+}
+
+gchar *feed_get_url(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->url;
+}
+
+/* Title */
+gchar *feed_get_title(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->title;
+}
+
+void feed_set_title(Feed *feed, gchar *new_title)
+{
+ g_return_if_fail(feed != NULL);
+ g_return_if_fail(new_title != NULL);
+
+ if (feed->title != NULL) {
+ g_free(feed->title);
+ feed->title = NULL;
+ }
+
+ feed->title = g_strdup(new_title);
+}
+
+/* Description */
+gchar *feed_get_description(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->description;
+}
+
+/* Language */
+gchar *feed_get_language(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->language;
+}
+
+/* Author */
+gchar *feed_get_author(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->author;
+}
+
+/* Generator */
+gchar *feed_get_generator(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->generator;
+}
+
+/* Fetch error (if not NULL, supplied by libcurl) */
+gchar *feed_get_fetcherror(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->fetcherr;
+}
+
+/* Returns number of items currently in the feed. */
+gint feed_n_items(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, -1);
+
+ if( feed->items == NULL ) /* No items here. */
+ return 0;
+
+ return g_slist_length(feed->items);
+}
+
+/* Returns nth item from feed. */
+FeedItem *feed_nth_item(Feed *feed, guint n)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+
+ return g_slist_nth_data(feed->items, n);
+}
+
+/* feed_update()
+ * Takes initialized feed with url set, fetches the feed from this url,
+ * updates rest of Feed struct members and returns HTTP response code
+ * we got from url's server. */
+guint feed_update(Feed *feed, time_t last_update)
+{
+ CURL *eh = NULL;
+ CURLcode res;
+ FeedParserCtx *feed_ctx = NULL;
+ glong response_code = 0;
+
+ g_return_val_if_fail(feed != NULL, FEED_ERR_NOFEED);
+ g_return_val_if_fail(feed->url != NULL, FEED_ERR_NOURL);
+
+ /* Init curl before anything else. */
+ eh = curl_easy_init();
+
+ g_return_val_if_fail(eh != NULL, FEED_ERR_INIT);
+
+ /* Curl initialized, create parser context now. */
+ feed_ctx = malloc( sizeof(FeedParserCtx) );
+
+ feed_ctx->parser = XML_ParserCreate(NULL);
+ feed_ctx->depth = 0;
+ feed_ctx->str = NULL;
+ feed_ctx->feed = feed;
+ feed_ctx->location = 0;
+ feed_ctx->curitem = NULL;
+ feed_ctx->id_is_permalink = TRUE;
+
+ feed_ctx->name = NULL;
+ feed_ctx->mail = NULL;
+
+ /* Set initial expat handlers, which will take care of choosing
+ * correct parser later. */
+ feed_parser_set_expat_handlers(feed_ctx);
+
+ curl_easy_setopt(eh, CURLOPT_URL, feed->url);
+ curl_easy_setopt(eh, CURLOPT_NOPROGRESS, 1);
+#ifdef CURLOPT_MUTE
+ curl_easy_setopt(eh, CURLOPT_MUTE, 1);
+#endif
+ curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, feed_writefunc);
+ curl_easy_setopt(eh, CURLOPT_WRITEDATA, feed_ctx);
+ curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(eh, CURLOPT_MAXREDIRS, 3);
+ curl_easy_setopt(eh, CURLOPT_TIMEOUT, feed->timeout);
+ curl_easy_setopt(eh, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(eh, CURLOPT_ENCODING, "");
+ curl_easy_setopt(eh, CURLOPT_USERAGENT, "libfeed 0.1");
+
+ /* Use HTTP's If-Modified-Since feature, if application provided
+ * the timestamp of last update. */
+ if( last_update != -1 ) {
+ curl_easy_setopt(eh, CURLOPT_TIMECONDITION,
+ CURL_TIMECOND_IFMODSINCE);
+ curl_easy_setopt(eh, CURLOPT_TIMEVALUE, last_update);
+ }
+
+#if LIBCURL_VERSION_NUM >= 0x070a00
+ if (feed->ssl_verify_peer == FALSE) {
+ curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);
+ }
+#endif
+
+ if(feed->cookies_path != NULL)
+ curl_easy_setopt(eh, CURLOPT_COOKIEFILE, feed->cookies_path);
+
+ res = curl_easy_perform(eh);
+ XML_Parse(feed_ctx->parser, "", 0, TRUE);
+
+ if( res != CURLE_OK ) {
+ feed->fetcherr = g_strdup(curl_easy_strerror(res));
+ response_code = FEED_ERR_FETCH;
+ } else
+ curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &response_code);
+
+ curl_easy_cleanup(eh);
+
+ /* Cleanup, we should be done. */
+ XML_ParserFree(feed_ctx->parser);
+ g_free(feed_ctx->name);
+ g_free(feed_ctx->mail);
+ g_free(feed_ctx);
+
+ return response_code;
+}
+
+void feed_foreach_item(Feed *feed, GFunc func, gpointer data)
+{
+ g_return_if_fail(feed != NULL);
+ g_return_if_fail(feed->items != NULL);
+
+ g_slist_foreach(feed->items, func, data);
+}
+
+gboolean feed_prepend_item(Feed *feed, FeedItem *item)
+{
+ g_return_val_if_fail(feed != NULL, FALSE);
+ g_return_val_if_fail(item != NULL, FALSE);
+
+ feed->items = g_slist_prepend(feed->items, item);
+ return TRUE;
+}
+
+gboolean feed_append_item(Feed *feed, FeedItem *item)
+{
+ g_return_val_if_fail(feed != NULL, FALSE);
+ g_return_val_if_fail(item != NULL, FALSE);
+
+ feed->items = g_slist_append(feed->items, item);
+ return TRUE;
+}
+
+gboolean feed_insert_item(Feed *feed, FeedItem *item, gint pos)
+{
+ g_return_val_if_fail(feed != NULL, FALSE);
+ g_return_val_if_fail(item != NULL, FALSE);
+ g_return_val_if_fail(pos < 0, FALSE);
+
+ feed->items = g_slist_insert(feed->items, item, pos);
+ return TRUE;
+}
+
+gchar *feed_get_cookies_path(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, NULL);
+ return feed->cookies_path;
+}
+
+void feed_set_cookies_path(Feed *feed, gchar *path)
+{
+ g_return_if_fail(feed != NULL);
+
+ if( feed->cookies_path != NULL ) {
+ g_free(feed->cookies_path);
+ feed->cookies_path = NULL;
+ }
+
+ feed->cookies_path = (path != NULL ? g_strdup(path) : NULL);
+}
+
+gboolean feed_get_ssl_verify_peer(Feed *feed)
+{
+ g_return_val_if_fail(feed != NULL, FALSE);
+ return feed->ssl_verify_peer;
+}
+
+void feed_set_ssl_verify_peer(Feed *feed, gboolean ssl_verify_peer)
+{
+ g_return_if_fail(feed != NULL);
+ feed->ssl_verify_peer = ssl_verify_peer;
+}
diff --git a/src/plugins/rssyl/libfeed/feed.h b/src/plugins/rssyl/libfeed/feed.h
new file mode 100644
index 0000000..dc8847d
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feed.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __FEED_H
+#define __FEED_H
+
+#include <glib.h>
+#include <expat.h>
+
+#define FEED_DEFAULT_TIMEOUT 20 /* In seconds */
+
+/* ---------------- Structures */
+
+typedef struct _Feed Feed;
+typedef struct _FeedItem FeedItem;
+typedef struct _FeedParserCtx FeedParserCtx;
+
+struct _Feed {
+ gchar *url;
+ gchar *title;
+ gchar *description;
+ gchar *language;
+ gchar *author;
+ gchar *generator;
+ time_t date;
+
+ guint timeout;
+ gchar *fetcherr;
+ gchar *cookies_path;
+ gboolean ssl_verify_peer;
+
+ GSList *items;
+};
+
+struct _FeedParserCtx {
+ XML_Parser parser;
+ guint depth;
+ guint location;
+ GString *str;
+ gchar *name;
+ gchar *mail;
+ gboolean id_is_permalink;
+
+ Feed *feed;
+ FeedItem *curitem;
+};
+
+typedef enum {
+ FEED_ERR_NOFEED,
+ FEED_ERR_NOURL,
+ FEED_ERR_INIT,
+ FEED_ERR_FETCH
+} FeedErrCodes;
+
+/* ---------------- Prototypes */
+
+Feed *feed_new(gchar *url);
+void feed_free(Feed *feed);
+
+void feed_free_items(Feed *feed);
+
+void feed_set_timeout(Feed *feed, guint timeout);
+guint feed_get_timeout(Feed *feed);
+
+void feed_set_url(Feed *feed, gchar *url);
+gchar *feed_get_url(Feed *feed);
+
+gchar *feed_get_title(Feed *feed);
+void feed_set_title(Feed *feed, gchar *new_title);
+
+gchar *feed_get_description(Feed *feed);
+gchar *feed_get_language(Feed *feed);
+gchar *feed_get_author(Feed *feed);
+gchar *feed_get_generator(Feed *feed);
+gchar *feed_get_fetcherror(Feed *feed);
+
+gchar *feed_get_cookies_path(Feed *feed);
+void feed_set_cookies_path(Feed *feed, gchar *path);
+
+gboolean feed_get_ssl_verify_peer(Feed *feed);
+void feed_set_ssl_verify_peer(Feed *feed, gboolean ssl_verify_peer);
+
+gint feed_n_items(Feed *feed);
+FeedItem *feed_nth_item(Feed *feed, guint n);
+
+void feed_foreach_item(Feed *feed, GFunc func, gpointer data);
+
+gboolean feed_prepend_item(Feed *feed, FeedItem *item);
+gboolean feed_append_item(Feed *feed, FeedItem *item);
+gboolean feed_insert_item(Feed *feed, FeedItem *item, gint pos);
+
+guint feed_update(Feed *feed, time_t last_update);
+
+#define FILL(n) g_free(n); n = g_strdup(text);
+
+#include "feeditem.h"
+
+#endif /* __FEED_H */
diff --git a/src/plugins/rssyl/libfeed/feeditem.c b/src/plugins/rssyl/libfeed/feeditem.c
new file mode 100644
index 0000000..d04831b
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feeditem.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#define __USE_GNU
+
+#include <stdlib.h>
+#include <glib.h>
+#include <curl/curl.h>
+#include <expat.h>
+
+#include "feed.h"
+#include "feeditem.h"
+#include "feeditemenclosure.h"
+#include "parser.h"
+
+/* feed_item_new()
+ * Initializes a new empty FeedItem struct, setting its parent feed,
+ * if supplied. */
+FeedItem *feed_item_new(Feed *feed)
+{
+ FeedItem *item = NULL;
+
+ item = malloc( sizeof(FeedItem) );
+ item->url = NULL;
+ item->title = NULL;
+ item->title_format = 0;
+ item->summary = NULL;
+ item->text = NULL;
+ item->author = NULL;
+ item->id = NULL;
+ item->comments_url = NULL;
+ item->parent_id = NULL;
+ item->enclosure = NULL;
+
+ item->sourcetitle = NULL;
+ item->sourceid = NULL;
+ item->sourcedate = -1;
+
+ item->id_is_permalink = FALSE;
+ item->xhtml_content = FALSE;
+
+ item->date_published = -1;
+ item->date_modified = -1;
+
+ if( feed != NULL )
+ item->feed = feed;
+
+ item->data = NULL;
+
+ return item;
+}
+
+void feed_item_free(FeedItem *item)
+{
+ if( item == NULL )
+ return;
+
+ g_free(item->url);
+ g_free(item->title);
+ g_free(item->summary);
+ g_free(item->text);
+ g_free(item->author);
+ g_free(item->id);
+ g_free(item->data);
+ g_free(item->comments_url);
+ g_free(item->parent_id);
+ g_free(item->enclosure);
+
+ g_free(item->sourcetitle);
+ g_free(item->sourceid);
+
+ g_free(item);
+ item = NULL;
+}
+
+Feed *feed_item_get_feed(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->feed;
+}
+
+/* URL */
+gchar *feed_item_get_url(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->url;
+}
+
+void feed_item_set_url(FeedItem *item, const gchar *url)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(url != NULL);
+
+ g_free(item->url);
+ item->url = g_strdup(url);
+}
+
+/* Title */
+gchar *feed_item_get_title(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->title;
+}
+
+void feed_item_set_title(FeedItem *item, const gchar *title)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(title != NULL);
+
+ g_free(item->title);
+ item->title = g_strdup(title);
+}
+
+/* Title format */
+gint feed_item_get_title_format(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, -1);
+ return item->title_format;
+}
+
+void feed_item_set_title_format(FeedItem *item, gint format)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(format >= 0 && format <= FEED_ITEM_TITLE_UNKNOWN);
+
+ item->title_format = format;
+}
+
+/* Summary */
+gchar *feed_item_get_summary(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->summary;
+}
+
+void feed_item_set_summary(FeedItem *item, const gchar *summary)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(summary != NULL);
+
+ g_free(item->summary);
+ item->summary = g_strdup(summary);
+}
+
+/* Text */
+gchar *feed_item_get_text(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->text;
+}
+
+void feed_item_set_text(FeedItem *item, const gchar *text)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(text != NULL);
+
+ g_free(item->text);
+ item->text = g_strdup(text);
+}
+
+/* Author */
+gchar *feed_item_get_author(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->author;
+}
+
+void feed_item_set_author(FeedItem *item, const gchar *author)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(author != NULL);
+
+ g_free(item->author);
+ item->author = g_strdup(author);
+}
+
+/* ID */
+gchar *feed_item_get_id(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->id;
+}
+
+void feed_item_set_id(FeedItem *item, const gchar *id)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(id != NULL);
+
+ g_free(item->id);
+ item->id = g_strdup(id);
+}
+
+/* Comments URL */
+gchar *feed_item_get_comments_url(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->comments_url;
+}
+
+void feed_item_set_comments_url(FeedItem *item, const gchar *url)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(url != NULL);
+
+ g_free(item->comments_url);
+ item->comments_url = g_strdup(url);
+}
+
+/* Comments URL */
+gchar *feed_item_get_parent_id(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->parent_id;
+}
+
+void feed_item_set_parent_id(FeedItem *item, const gchar *id)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(id != NULL);
+
+ g_free(item->parent_id);
+ item->parent_id = g_strdup(id);
+}
+
+/* Media enclosure */
+FeedItemEnclosure *feed_item_get_enclosure(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->enclosure;
+}
+
+void feed_item_set_enclosure(FeedItem *item, FeedItemEnclosure *enclosure)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(enclosure != NULL);
+ g_return_if_fail(enclosure->url != NULL);
+ g_return_if_fail(enclosure->type != NULL);
+
+ feed_item_enclosure_free(item->enclosure);
+ item->enclosure = enclosure;
+}
+
+/* Source title (Atom only) */
+gchar *feed_item_get_sourcetitle(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->sourcetitle;
+}
+
+void feed_item_set_sourcetitle(FeedItem *item, const gchar *sourcetitle)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(sourcetitle != NULL);
+
+ g_free(item->sourcetitle);
+ item->sourcetitle = g_strdup(sourcetitle);
+}
+
+/* Source ID (Atom only) */
+gchar *feed_item_get_sourceid(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, NULL);
+ return item->sourceid;
+}
+
+void feed_item_set_sourceid(FeedItem *item, const gchar *sourceid)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(sourceid != NULL);
+
+ g_free(item->sourceid);
+ item->sourceid = g_strdup(sourceid);
+}
+
+/* Source date (Atom only) */
+time_t feed_item_get_sourcedate(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, -1);
+ return item->sourcedate;
+}
+
+void feed_item_set_sourcedate(FeedItem *item, time_t date)
+{
+ g_return_if_fail(item != NULL);
+ item->sourcedate = date;
+}
+
+/* Date published */
+time_t feed_item_get_date_published(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, -1);
+ return item->date_published;
+}
+
+void feed_item_set_date_published(FeedItem *item, time_t date)
+{
+ g_return_if_fail(item != NULL);
+ item->date_published = date;
+}
+
+/* Date modified */
+time_t feed_item_get_date_modified(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, -1);
+ return item->date_modified;
+}
+
+void feed_item_set_date_modified(FeedItem *item, time_t date)
+{
+ g_return_if_fail(item != NULL);
+ item->date_modified = date;
+}
+
+FeedItem *feed_item_copy(FeedItem *item)
+{
+ FeedItem *nitem;
+
+ g_return_val_if_fail(item != NULL, NULL);
+
+ nitem = feed_item_new(NULL);
+
+ nitem->url = g_strdup(item->url);
+ nitem->title = g_strdup(item->title);
+ nitem->summary = g_strdup(item->summary);
+ nitem->text = g_strdup(item->text);
+ nitem->author = g_strdup(item->author);
+ nitem->id = g_strdup(item->id);
+ nitem->comments_url = g_strdup(item->comments_url);
+ nitem->parent_id = g_strdup(item->parent_id);
+
+ nitem->enclosure = g_memdup(item->enclosure, sizeof(FeedItemEnclosure));
+
+ nitem->date_published = item->date_published;
+ nitem->date_modified = item->date_modified;
+
+ nitem->id_is_permalink = item->id_is_permalink;
+ nitem->xhtml_content = item->xhtml_content;
+
+ nitem->data = g_memdup(item->data, sizeof(item->data));
+
+ return nitem;
+}
+
+gboolean feed_item_id_is_permalink(FeedItem *item)
+{
+ g_return_val_if_fail(item != NULL, FALSE);
+
+ return item->id_is_permalink;
+}
+
+void feed_item_set_id_permalink(FeedItem *item, gboolean permalink)
+{
+ g_return_if_fail(item != NULL);
+
+ item->id_is_permalink = permalink;
+}
diff --git a/src/plugins/rssyl/libfeed/feeditem.h b/src/plugins/rssyl/libfeed/feeditem.h
new file mode 100644
index 0000000..9125af4
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feeditem.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __FEEDITEM_H
+#define __FEEDITEM_H
+
+#include "feed.h"
+#include "feeditemenclosure.h"
+
+struct _FeedItem {
+ gchar *url;
+ gchar *title;
+ gint title_format;
+ gchar *summary;
+ gchar *text;
+ gchar *author;
+ gchar *id;
+ gchar *comments_url;
+ gchar *parent_id;
+
+ gchar *sourceid;
+ gchar *sourcetitle;
+ time_t sourcedate;
+
+ gboolean id_is_permalink;
+ gboolean xhtml_content;
+
+ FeedItemEnclosure *enclosure;
+
+ time_t date_published;
+ time_t date_modified;
+
+ Feed *feed; /* feed we belong to */
+
+ gpointer data; /* application-specific data */
+};
+
+#define FEED_ITEM_TITLE_TEXT 0
+#define FEED_ITEM_TITLE_HTML 1
+#define FEED_ITEM_TITLE_XHTML 2
+#define FEED_ITEM_TITLE_UNKNOWN 3
+
+FeedItem *feed_item_new(Feed *feed);
+void feed_item_free(FeedItem *item);
+
+Feed *feed_item_get_feed(FeedItem *item);
+
+gchar *feed_item_get_url(FeedItem *item);
+void feed_item_set_url(FeedItem *item, const gchar *url);
+
+gchar *feed_item_get_title(FeedItem *item);
+void feed_item_set_title(FeedItem *item, const gchar *title);
+
+gint feed_item_get_title_format(FeedItem *item);
+void feed_item_set_title_format(FeedItem *item, gint format);
+
+gchar *feed_item_get_summary(FeedItem *item);
+void feed_item_set_summary(FeedItem *item, const gchar *summary);
+
+gchar *feed_item_get_text(FeedItem *item);
+void feed_item_set_text(FeedItem *item, const gchar *text);
+
+gchar *feed_item_get_author(FeedItem *item);
+void feed_item_set_author(FeedItem *item, const gchar *author);
+
+gchar *feed_item_get_id(FeedItem *item);
+void feed_item_set_id(FeedItem *item, const gchar *id);
+
+gchar *feed_item_get_comments_url(FeedItem *item);
+void feed_item_set_comments_url(FeedItem *item, const gchar *url);
+
+gchar *feed_item_get_parent_id(FeedItem *item);
+void feed_item_set_parent_id(FeedItem *item, const gchar *id);
+
+FeedItemEnclosure *feed_item_get_enclosure(FeedItem *item);
+void feed_item_set_enclosure(FeedItem *item, FeedItemEnclosure *enclosure);
+
+gchar *feed_item_get_sourcetitle(FeedItem *item);
+void feed_item_set_sourcetitle(FeedItem *item, const gchar *sourcetitle);
+
+gchar *feed_item_get_sourceid(FeedItem *item);
+void feed_item_set_sourceid(FeedItem *item, const gchar *sourceid);
+
+time_t feed_item_get_sourcedate(FeedItem *item);
+void feed_item_set_sourcedate(FeedItem *item, time_t date);
+
+time_t feed_item_get_date_published(FeedItem *item);
+void feed_item_set_date_published(FeedItem *item, time_t date);
+
+time_t feed_item_get_date_modified(FeedItem *item);
+void feed_item_set_date_modified(FeedItem *item, time_t date);
+
+FeedItem *feed_item_copy(FeedItem *item);
+
+gboolean feed_item_id_is_permalink(FeedItem *item);
+void feed_item_set_id_permalink(FeedItem *item, gboolean permalink);
+
+#endif /* __FEEDITEM_H */
diff --git a/src/plugins/rssyl/libfeed/feeditemenclosure.c b/src/plugins/rssyl/libfeed/feeditemenclosure.c
new file mode 100644
index 0000000..183cfb5
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feeditemenclosure.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#define __USE_GNU
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include "feeditemenclosure.h"
+
+FeedItemEnclosure *feed_item_enclosure_new(gchar *url, gchar *type, gulong size)
+{
+ FeedItemEnclosure *enclosure = NULL;
+
+ g_return_val_if_fail(url != NULL, NULL);
+ g_return_val_if_fail(type != NULL, NULL);
+ g_return_val_if_fail(size > 0, NULL);
+
+ enclosure = malloc( sizeof(FeedItemEnclosure) );
+ enclosure->url = g_strdup(url);
+ enclosure->type = g_strdup(type);
+ enclosure->size = size;
+
+ return enclosure;
+}
+
+void feed_item_enclosure_free(FeedItemEnclosure *enclosure)
+{
+ if( enclosure == NULL )
+ return;
+
+ g_free(enclosure->url);
+ g_free(enclosure->type);
+
+ g_free(enclosure);
+ enclosure = NULL;
+}
+
+/* URL */
+gchar *feed_item_enclosure_get_url(FeedItemEnclosure *enclosure)
+{
+ g_return_val_if_fail(enclosure != NULL, NULL);
+ return enclosure->url;
+}
+
+void feed_item_enclosure_set_url(FeedItemEnclosure *enclosure, gchar *url)
+{
+ g_return_if_fail(enclosure != NULL);
+ g_return_if_fail(url != NULL);
+
+ g_free(enclosure->url);
+ enclosure->url = g_strdup(url);
+}
+
+/* Type */
+gchar *feed_item_enclosure_get_type(FeedItemEnclosure *enclosure)
+{
+ g_return_val_if_fail(enclosure != NULL, NULL);
+ return enclosure->type;
+}
+
+void feed_item_enclosure_set_type(FeedItemEnclosure *enclosure, gchar *type)
+{
+ g_return_if_fail(enclosure != NULL);
+ g_return_if_fail(type != NULL);
+
+ g_free(enclosure->type);
+ enclosure->type = g_strdup(type);
+}
+
+/* Size */
+gulong feed_item_enclosure_get_size(FeedItemEnclosure *enclosure)
+{
+ g_return_val_if_fail(enclosure != NULL, -1);
+ return enclosure->size;
+}
+
+void feed_item_enclosure_set_size(FeedItemEnclosure *enclosure, gulong size)
+{
+ g_return_if_fail(enclosure != NULL);
+ g_return_if_fail(size > 0);
+
+ enclosure->size = size;
+}
diff --git a/src/plugins/rssyl/libfeed/feeditemenclosure.h b/src/plugins/rssyl/libfeed/feeditemenclosure.h
new file mode 100644
index 0000000..ba4d127
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/feeditemenclosure.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __FEEDITEMENCLOSURE_H
+#define __FEEDITEMENCLOSURE_H
+
+struct _FeedItemEnclosure {
+ gchar *url;
+ gchar *type;
+ gulong size;
+};
+
+typedef struct _FeedItemEnclosure FeedItemEnclosure;
+
+FeedItemEnclosure *feed_item_enclosure_new(gchar *url, gchar *type, gulong size);
+void feed_item_enclosure_free(FeedItemEnclosure *enclosure);
+
+gchar *feed_item_enclosure_get_url(FeedItemEnclosure *enclosure);
+void feed_item_enclosure_set_url(FeedItemEnclosure *enclosure, gchar *url);
+
+gchar *feed_item_enclosure_get_type(FeedItemEnclosure *enclosure);
+void feed_item_enclosure_set_type(FeedItemEnclosure *enclosure, gchar *type);
+
+gulong feed_item_enclosure_get_size(FeedItemEnclosure *enclosure);
+void feed_item_enclosure_set_size(FeedItemEnclosure *enclosure, gulong size);
+
+#endif /* __FEEDITEMENCLOSURE_H */
diff --git a/src/plugins/rssyl/libfeed/parser.c b/src/plugins/rssyl/libfeed/parser.c
new file mode 100644
index 0000000..1ebc412
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#include <glib.h>
+#include <curl/curl.h>
+#include <expat.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <codeconv.h>
+
+#include "feed.h"
+
+#include "parser.h"
+
+static void _handler_set(XML_Parser parser, guint type)
+{
+ if( parser == NULL )
+ return;
+
+ switch(type) {
+ case FEED_TYPE_RSS_20:
+ XML_SetElementHandler(parser,
+ feed_parser_rss20_start,
+ feed_parser_rss20_end);
+ break;
+
+ case FEED_TYPE_RDF:
+ XML_SetElementHandler(parser,
+ feed_parser_rdf_start,
+ feed_parser_rdf_end);
+ break;
+
+ case FEED_TYPE_ATOM_10:
+ XML_SetElementHandler(parser,
+ feed_parser_atom10_start,
+ feed_parser_atom10_end);
+ break;
+ }
+}
+
+static void _elparse_start_chooser(void *data,
+ const gchar *el, const gchar **attr)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ guint feedtype = FEED_TYPE_NONE;
+ gchar *version;
+
+ if( ctx->depth == 0 ) {
+
+ /* RSS 2.0 detected */
+ if( !strcmp(el, "rss") ) {
+ feedtype = FEED_TYPE_RSS_20;
+ } else if( !strcmp(el, "rdf:RDF") ) {
+ feedtype = FEED_TYPE_RDF;
+ } else if( !strcmp(el, "feed") ) {
+
+ /* ATOM feed detected, let's check version */
+ version = feed_parser_get_attribute_value(attr, "xmlns");
+ if( !strcmp(version, "http://www.w3.org/2005/Atom") ||
+ !strcmp(version, "https://www.w3.org/2005/Atom") )
+ feedtype = FEED_TYPE_ATOM_10;
+ else
+ feedtype = FEED_TYPE_ATOM_03;
+ }
+ }
+
+ _handler_set(ctx->parser, feedtype);
+
+ ctx->depth++;
+}
+
+static void _elparse_end_dummy(void *data, const gchar *el)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+
+ if( ctx->str != NULL ) {
+ g_string_free(ctx->str, TRUE);
+ ctx->str = NULL;
+ }
+
+ ctx->depth--;
+}
+
+void libfeed_expat_chparse(void *data, const gchar *s, gint len)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ gchar *buf = NULL;
+ gint i, xblank = 1;
+
+ buf = malloc(len+1);
+ strncpy(buf, s, len);
+ buf[len] = '\0';
+
+ /* check if the string is blank, ... */
+ for( i = 0; i < strlen(buf); i++ )
+ if( !isspace(buf[i]) )
+ xblank = 0;
+
+ /* ...because we do not want the blanks if we're just starting new GString */
+ if( xblank > 0 && ctx->str == NULL ) {
+ g_free(buf);
+ return;
+ }
+
+ if( ctx->str == NULL ) {
+ ctx->str = g_string_sized_new(len + 1);
+ }
+
+ g_string_append(ctx->str, buf);
+ g_free(buf);
+}
+
+
+void feed_parser_set_expat_handlers(FeedParserCtx *ctx)
+{
+ XML_SetUserData(ctx->parser, (void *)ctx);
+
+ XML_SetElementHandler(ctx->parser,
+ _elparse_start_chooser,
+ _elparse_end_dummy);
+
+ XML_SetCharacterDataHandler(ctx->parser,
+ libfeed_expat_chparse);
+
+ XML_SetUnknownEncodingHandler(ctx->parser, feed_parser_unknown_encoding_handler,
+ NULL);
+}
+
+size_t feed_writefunc(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ gint len = size * nmemb;
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ gint status, err;
+
+ status = XML_Parse(ctx->parser, ptr, len, FALSE);
+
+ if( status == XML_STATUS_ERROR ) {
+ err = XML_GetErrorCode(ctx->parser);
+ printf("\nExpat: --- %s\n\n", XML_ErrorString(err));
+ }
+
+ return len;
+}
+
+gchar *feed_parser_get_attribute_value(const gchar **attr, const gchar *name)
+{
+ guint i;
+
+ if( attr == NULL && name == NULL )
+ return NULL;
+
+ for( i = 0; attr[i] != NULL && attr[i+1] != NULL; i += 2 ) {
+ if( !strcmp( attr[i], name) )
+ return (gchar *)attr[i+1];
+ }
+
+ /* We haven't found anything. */
+ return NULL;
+}
+
+#define CHARSIZEUTF32 4
+
+enum {
+ LEP_ICONV_OK,
+ LEP_ICONV_FAILED,
+ LEP_ICONV_ILSEQ,
+ LEP_ICONV_INVAL,
+ LEP_ICONV_UNKNOWN
+};
+
+static gint giconv_utf32_char(GIConv cd, const gchar *inbuf, size_t insize,
+ guint32 *p_value)
+{
+#ifdef HAVE_ICONV
+ size_t outsize;
+ guchar outbuf[CHARSIZEUTF32];
+ gchar *outbufp;
+ gint r, errno;
+
+ outsize = sizeof(outbuf);
+ outbufp = (gchar *)outbuf;
+#ifdef HAVE_ICONV_PROTO_CONST
+ r = g_iconv(cd, (const gchar **)&inbuf, &insize,
+ &outbufp, &outsize);
+#else
+ r = g_iconv(cd, (gchar **)&inbuf, &insize,
+ &outbufp, &outsize);
+#endif
+ if( r == -1 ) {
+ g_iconv(cd, 0, 0, 0, 0);
+ switch(errno) {
+ case EILSEQ:
+ return LEP_ICONV_ILSEQ;
+ case EINVAL:
+ return LEP_ICONV_INVAL;
+ default:
+ return LEP_ICONV_UNKNOWN;
+ }
+ } else {
+ guint32 value;
+ guint i;
+
+ if( (insize > 0) || (outsize > 0) )
+ return LEP_ICONV_FAILED;
+
+ value = 0;
+ for( i = 0; i < sizeof(outbuf); i++ ) {
+ value = (value << 8) + outbuf[i];
+ }
+ *p_value = value;
+ return LEP_ICONV_OK;
+ }
+#else
+ return LEP_ICONV_FAILED;
+#endif
+}
+
+static gint feed_parser_setup_unknown_encoding(const gchar *charset,
+ XML_Encoding *info)
+{
+ GIConv cd;
+ gint flag, r;
+ gchar buf[4];
+ guint i, j, k;
+ guint32 value;
+
+ cd = g_iconv_open("UTF-32BE", charset);
+ if( cd == (GIConv) -1 )
+ return -1;
+
+ flag = 0;
+ for( i = 0; i < 256; i++ ) {
+ /* first char */
+ buf[0] = i;
+ info->map[i] = 0;
+ r = giconv_utf32_char(cd, buf, 1, &value);
+ if( r == LEP_ICONV_OK) {
+ info->map[i] = value;
+ } else if( r != LEP_ICONV_INVAL ) {
+ } else {
+ for( j = 0; j < 256; j++ ) {
+ /* second char */
+ buf[1] = j;
+ r = giconv_utf32_char(cd, buf, 2, &value);
+ if( r == LEP_ICONV_OK ) {
+ flag = 1;
+ info->map[i] = -2;
+ } else if( r != LEP_ICONV_INVAL ) {
+ } else {
+ for( k = 0; k < 256; k++ ) {
+ /* third char */
+ buf[2] = k;
+ r = giconv_utf32_char(cd, buf, 3, &value);
+ if( r == LEP_ICONV_OK) {
+ info->map[i] = -3;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ g_iconv_close(cd);
+
+ return flag;
+}
+
+struct FeedParserUnknownEncoding {
+ gchar *charset;
+ GIConv cd;
+};
+
+static gint feed_parser_unknown_encoding_convert(void *data, const gchar *s)
+{
+ gint r;
+ struct FeedParserUnknownEncoding *enc_data;
+ size_t insize;
+ guint32 value;
+
+ enc_data = data;
+ insize = 4;
+
+ if( s == NULL )
+ return -1;
+
+ r = giconv_utf32_char(enc_data->cd, s, insize, &value);
+ if( r != LEP_ICONV_OK )
+ return -1;
+
+ return 0;
+}
+
+static void feed_parser_unknown_encoding_data_free(void *data)
+{
+ struct FeedParserUnknownEncoding *enc_data;
+
+ enc_data = data;
+ free(enc_data->charset);
+ g_iconv_close(enc_data->cd);
+ free(enc_data);
+}
+
+int feed_parser_unknown_encoding_handler(void *encdata, const XML_Char *name,
+ XML_Encoding *info)
+{
+ GIConv cd;
+ struct FeedParserUnknownEncoding *data;
+ int result;
+
+ result = feed_parser_setup_unknown_encoding(name, info);
+ if( result == 0 ) {
+ info->data = NULL;
+ info->convert = NULL;
+ info->release = NULL;
+ return XML_STATUS_OK;
+ }
+
+ cd = g_iconv_open("UTF-32BE", name);
+ if( cd == (GIConv)-1 )
+ return XML_STATUS_ERROR;
+
+ data = malloc( sizeof(*data) );
+ if( data == NULL ) {
+ g_iconv_close(cd);
+ return XML_STATUS_ERROR;
+ }
+
+ data->charset = strdup(name);
+ if( data->charset == NULL ) {
+ free(data);
+ g_iconv_close(cd);
+ return XML_STATUS_ERROR;
+ }
+
+ data->cd = cd;
+ info->data = data;
+ info->convert = feed_parser_unknown_encoding_convert;
+ info->release = feed_parser_unknown_encoding_data_free;
+
+ return XML_STATUS_OK;
+}
diff --git a/src/plugins/rssyl/libfeed/parser.h b/src/plugins/rssyl/libfeed/parser.h
new file mode 100644
index 0000000..f8cbb9a
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __PARSER_H
+
+#include "feed.h"
+#include "parser_rss20.h"
+#include "parser_rdf.h"
+#include "parser_atom10.h"
+
+void libfeed_expat_chparse(void *data, const gchar *s, gint len);
+void feed_parser_set_expat_handlers(FeedParserCtx *ctx);
+size_t feed_writefunc(void *ptr, size_t size, size_t nmemb, void *stream);
+gchar *feed_parser_get_attribute_value(const gchar **attr, const gchar *name);
+
+int feed_parser_unknown_encoding_handler(void *encdata, const XML_Char *name,
+ XML_Encoding *info);
+
+
+enum {
+ FEED_TYPE_NONE,
+ FEED_TYPE_RDF,
+ FEED_TYPE_RSS_20,
+ FEED_TYPE_ATOM_03,
+ FEED_TYPE_ATOM_10,
+ FEED_TYPE_OPML
+} FeedTypes;
+
+#endif /* __PARSER_H */
diff --git a/src/plugins/rssyl/libfeed/parser_atom10.c b/src/plugins/rssyl/libfeed/parser_atom10.c
new file mode 100644
index 0000000..59fba1c
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_atom10.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+#define __USE_GNU
+
+#include <glib.h>
+#include <expat.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "feed.h"
+#include "feeditem.h"
+#include "date.h"
+#include "parser.h"
+#include "parser_atom10.h"
+
+void feed_parser_atom10_start(void *data, const gchar *el, const gchar **attr)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ gchar *a = NULL;
+
+ if( ctx->depth == 1 ) {
+
+ if( !strcmp(el, "entry") ) {
+ /* Start of new feed item found.
+ * Create a new FeedItem, freeing the one we already have, if any. */
+ if( ctx->curitem != NULL )
+ feed_item_free(ctx->curitem);
+ ctx->curitem = feed_item_new(ctx->feed);
+ ctx->location = FEED_LOC_ATOM10_ENTRY;
+ } else if( !strcmp(el, "author") ) {
+ /* Start of author info for the feed found.
+ * Set correct location. */
+ ctx->location = FEED_LOC_ATOM10_AUTHOR;
+ } else ctx->location = FEED_LOC_ATOM10_NONE;
+
+ } else if( ctx->depth == 2 ) {
+
+ if( !strcmp(el, "author") ) {
+ /* Start of author info for current feed item.
+ * Set correct location. */
+ ctx->location = FEED_LOC_ATOM10_AUTHOR;
+ } else if( !strcmp(el, "link") ) {
+ /* Capture item URL, from the "url" XML attribute. */
+ if (ctx->curitem && ctx->location == FEED_LOC_ATOM10_ENTRY)
+ ctx->curitem->url = g_strdup(feed_parser_get_attribute_value(attr, "href"));
+ } else if( !strcmp(el, "source") ) {
+ ctx->location = FEED_LOC_ATOM10_SOURCE;
+ } else ctx->location = FEED_LOC_ATOM10_ENTRY;
+
+ if( !strcmp(el, "title") ) {
+ a = feed_parser_get_attribute_value(attr, "type");
+ if( !a || !strcmp(a, "text") )
+ ctx->curitem->title_format = FEED_ITEM_TITLE_TEXT;
+ else if( !strcmp(a, "html") )
+ ctx->curitem->title_format = FEED_ITEM_TITLE_HTML;
+ else if( !strcmp(a, "xhtml") )
+ ctx->curitem->title_format = FEED_ITEM_TITLE_XHTML;
+ else
+ ctx->curitem->title_format = FEED_ITEM_TITLE_UNKNOWN;
+ } else if (!strcmp(el, "content") ) {
+ a = feed_parser_get_attribute_value(attr, "type");
+ if (a && !strcmp(a, "xhtml")) {
+ ctx->curitem->xhtml_content = TRUE;
+ ctx->location = FEED_LOC_ATOM10_CONTENT;
+ }
+ }
+ }
+
+ ctx->depth++;
+}
+
+void feed_parser_atom10_end(void *data, const gchar *el)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ Feed *feed = ctx->feed;
+ gchar *text = NULL;
+
+ if( ctx->str != NULL )
+ text = ctx->str->str;
+
+ ctx->depth--;
+
+ switch( ctx->depth ) {
+
+ case 0:
+
+ if( !strcmp(el, "feed") ) {
+ /* We have finished parsing the feed, reverse the list
+ * so it's not upside down. */
+ feed->items = g_slist_reverse(ctx->feed->items);
+ }
+
+ break;
+
+ case 1:
+
+ /* decide if we just received </entry>, so we can
+ * add a complete item to feed */
+ if( !strcmp(el, "entry") ) {
+
+ /* append the complete feed item */
+ if( ctx->curitem->id && ctx->curitem->title
+ && ctx->curitem->date_modified ) {
+ feed->items =
+ g_slist_prepend(feed->items, (gpointer)ctx->curitem);
+ }
+
+ /* since it's in the linked list, lose this pointer */
+ ctx->curitem = NULL;
+
+ } else if( !strcmp(el, "title") ) { /* so it wasn't end of item */
+ FILL(feed->title)
+ } else if( !strcmp(el, "summary" ) ) {
+ FILL(feed->description)
+ } else if( !strcmp(el, "updated" ) ) {
+ feed->date = parseISO8601Date(text);
+ }
+ /* FIXME: add more later */
+
+ break;
+
+ case 2:
+
+ if( ctx->curitem == NULL )
+ break;
+
+ switch(ctx->location) {
+
+ /* We're in feed/entry */
+ case FEED_LOC_ATOM10_ENTRY:
+ if( !strcmp(el, "title") ) {
+ FILL(ctx->curitem->title)
+ } else if( !strcmp(el, "summary") ) {
+ FILL(ctx->curitem->summary)
+ } else if( !strcmp(el, "content") ) {
+ if (!ctx->curitem->xhtml_content)
+ FILL(ctx->curitem->text);
+ } else if( !strcmp(el, "id") ) {
+ FILL(ctx->curitem->id);
+ feed_item_set_id_permalink(ctx->curitem, TRUE);
+ } else if( !strcmp(el, "published") ) {
+ ctx->curitem->date_published = parseISO8601Date(text);
+ } else if( !strcmp(el, "updated") ) {
+ ctx->curitem->date_modified = parseISO8601Date(text);
+ }
+
+ break;
+
+ /* We're in feed/author or about to leave feed/entry/author */
+ case FEED_LOC_ATOM10_AUTHOR:
+ if( !strcmp(el, "author" ) ) {
+ /* We just finished parsing <author> */
+ ctx->curitem->author = g_strdup_printf("%s%s%s%s%s",
+ ctx->name ? ctx->name : "",
+ ctx->name && ctx->mail ? " <" : ctx->mail ? "<" : "",
+ ctx->mail ? ctx->mail : "",
+ ctx->mail ? ">" : "",
+ !ctx->name && !ctx->mail ? "N/A" : "");
+ ctx->location = FEED_LOC_ATOM10_ENTRY;
+ } else if( !strcmp(el, "name") ) {
+ FILL(feed->author);
+ }
+
+ break;
+ }
+
+ break;
+
+ case 3:
+
+ if( ctx->curitem == NULL )
+ break;
+
+ switch(ctx->location) {
+
+ /* We're in feed/entry/author */
+ case FEED_LOC_ATOM10_AUTHOR:
+ if( !strcmp(el, "name") ) {
+ FILL(ctx->name);
+ } else if( !strcmp(el, "email") ) {
+ FILL(ctx->mail);
+ }
+
+ break;
+
+ /* We're in feed/entry/source */
+ case FEED_LOC_ATOM10_SOURCE:
+ if( !strcmp(el, "title" ) ) {
+ FILL(ctx->curitem->sourcetitle);
+ } else if( !strcmp(el, "id" ) ) {
+ FILL(ctx->curitem->sourceid);
+ } else if( !strcmp(el, "updated" ) ) {
+ ctx->curitem->sourcedate = parseISO8601Date(text);
+ }
+
+ break;
+
+ case FEED_LOC_ATOM10_CONTENT:
+ if (!strcmp(el, "div") && ctx->curitem->xhtml_content)
+ FILL(ctx->curitem->text);
+ break;
+
+ }
+
+
+ break;
+ }
+
+ if( ctx->str != NULL ) {
+ g_string_free(ctx->str, TRUE);
+ ctx->str = NULL;
+ }
+ ctx->str = NULL;
+}
diff --git a/src/plugins/rssyl/libfeed/parser_atom10.h b/src/plugins/rssyl/libfeed/parser_atom10.h
new file mode 100644
index 0000000..85e95b4
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_atom10.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __PARSER_ATOM03_H
+#define __PARSER_ATOM03_H
+
+void feed_parser_atom10_start(void *data, const char *el, const char **attr);
+void feed_parser_atom10_end(void *data, const char *el);
+
+enum {
+ FEED_LOC_ATOM10_NONE,
+ FEED_LOC_ATOM10_ENTRY,
+ FEED_LOC_ATOM10_AUTHOR,
+ FEED_LOC_ATOM10_SOURCE,
+ FEED_LOC_ATOM10_CONTENT
+} FeedAtom10Locations;
+
+#endif /* __PARSER_ATOM03_H */
diff --git a/src/plugins/rssyl/libfeed/parser_opml.c b/src/plugins/rssyl/libfeed/parser_opml.c
new file mode 100644
index 0000000..09e34aa
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_opml.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#include <glib.h>
+#include <curl/curl.h>
+#include <expat.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <codeconv.h>
+
+#include "feed.h"
+
+#include "parser.h"
+#include "parser_opml.h"
+
+static void _opml_parser_start(void *data, const gchar *el, const gchar **attr)
+{
+ OPMLProcessCtx *ctx = (OPMLProcessCtx *)data;
+ gchar *title = NULL, *type = NULL, *url = NULL, *tmp = NULL;
+
+ if( ctx->body_reached ) {
+ if( ctx->depth >= 2 && !strcmp(el, "outline") ) {
+ title = feed_parser_get_attribute_value(attr, "title");
+ type = feed_parser_get_attribute_value(attr, "type");
+ if( type != NULL && strcmp(type, "folder") ) {
+ url = feed_parser_get_attribute_value(attr, "xmlUrl");
+
+ if( url != NULL ) {
+ if( !strncmp(url, "feed://", 7) )
+ tmp = g_strdup(url+7);
+ else if( !strncmp(url, "feed:", 5) )
+ tmp = g_strdup(url+5);
+
+ if( tmp != NULL ) {
+ g_free(url);
+ url = tmp;
+ }
+ }
+ }
+
+ if( ctx->user_function != NULL ) {
+ ctx->user_function(title, url, ctx->depth, ctx->user_data);
+ }
+ }
+ }
+
+ if( ctx->depth == 1 ) {
+ if( !strcmp(el, "body") ) {
+ ctx->body_reached = TRUE;
+ }
+ }
+
+ ctx->depth++;
+}
+
+static void _opml_parser_end(void *data, const gchar *el)
+{
+ OPMLProcessCtx *ctx = (OPMLProcessCtx *)data;
+
+ ctx->depth--;
+}
+
+void opml_process(gchar *path, OPMLProcessFunc function, gpointer data)
+{
+ OPMLProcessCtx *ctx = NULL;
+ gchar *contents = NULL;
+ GError *error = NULL;
+ gint status, err;
+
+ /* Initialize our context */
+ ctx = malloc( sizeof(OPMLProcessCtx) );
+ ctx->parser = XML_ParserCreate(NULL);
+ ctx->depth = 0;
+ ctx->str = NULL;
+ ctx->user_function = function;
+ ctx->body_reached = FALSE;
+ ctx->user_data = data;
+
+ /* Set expat parser handlers */
+ XML_SetUserData(ctx->parser, (void *)ctx);
+ XML_SetElementHandler(ctx->parser,
+ _opml_parser_start,
+ _opml_parser_end);
+ XML_SetCharacterDataHandler(ctx->parser, libfeed_expat_chparse);
+ XML_SetUnknownEncodingHandler(ctx->parser,
+ feed_parser_unknown_encoding_handler, NULL);
+
+ g_file_get_contents(path, &contents, NULL, &error);
+
+ if( error || !contents )
+ return;
+
+/*
+ lines = g_strsplit(contents, '\n', 0);
+
+ while( lines[i] ) {
+ status = XML_Parse(ctx->parser, lines[i], strlen(lines[i]), FALSE);
+ if( status == XML_STATUS_ERROR ) {
+ err = XML_GetErrorCode(ctx->parser);
+ sprintf(stderr, "\nExpat: --- %s\n\n", XML_ErrorString(err));
+ }
+ }
+*/
+
+ status = XML_Parse(ctx->parser, contents, strlen(contents), FALSE);
+ err = XML_GetErrorCode(ctx->parser);
+ fprintf(stderr, "\nExpat: --- %s (%s)\n\n", XML_ErrorString(err),
+ (status == XML_STATUS_OK ? "OK" : "NOT OK"));
+
+ XML_Parse(ctx->parser, "", 0, TRUE);
+
+ XML_ParserFree(ctx->parser);
+ g_free(ctx);
+}
diff --git a/src/plugins/rssyl/libfeed/parser_opml.h b/src/plugins/rssyl/libfeed/parser_opml.h
new file mode 100644
index 0000000..518b072
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_opml.h
@@ -0,0 +1,23 @@
+#ifndef __PARSER_OPML
+#define __PARSER_OPML
+
+#include <expat.h>
+
+typedef void (*OPMLProcessFunc) (gchar *title, gchar *url, gint depth,
+ gpointer data);
+
+struct _OPMLProcessCtx {
+ XML_Parser parser;
+ guint depth;
+ guint prevdepth;
+ GString *str;
+ OPMLProcessFunc user_function;
+ gboolean body_reached;
+ gpointer user_data;
+};
+
+typedef struct _OPMLProcessCtx OPMLProcessCtx;
+
+void opml_process(gchar *path, OPMLProcessFunc function, gpointer data);
+
+#endif /* __PARSER_OPML */
diff --git a/src/plugins/rssyl/libfeed/parser_rdf.c b/src/plugins/rssyl/libfeed/parser_rdf.c
new file mode 100644
index 0000000..dc424c5
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_rdf.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+#define __USE_GNU
+
+#include <glib.h>
+#include <expat.h>
+#include <string.h>
+
+#include "feed.h"
+#include "date.h"
+#include "parser_rdf.h"
+
+void feed_parser_rdf_start(void *data, const gchar *el, const gchar **attr)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+
+ if( ctx->depth == 1 ) {
+ if( !strcmp(el, "channel") ) {
+ ctx->location = FEED_LOC_RDF_CHANNEL;
+ } else if( !strcmp(el, "item") ) {
+
+ if( ctx->curitem != NULL )
+ feed_item_free(ctx->curitem);
+
+ ctx->curitem = feed_item_new(ctx->feed);
+ ctx->location = FEED_LOC_RDF_ITEM;
+
+ } else ctx->location = 0;
+ }
+
+ ctx->depth++;
+
+}
+
+void feed_parser_rdf_end(void *data, const gchar *el)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ Feed *feed = ctx->feed;
+ gchar *text = NULL;
+
+ if( ctx->str != NULL )
+ text = ctx->str->str;
+
+ ctx->depth--;
+
+ switch( ctx->depth ) {
+
+ case 0:
+
+ if( !strcmp(el, "rdf") ) {
+ /* we finished parsing the feed */
+ ctx->feed->items = g_slist_reverse(ctx->feed->items);
+ }
+
+ break;
+
+ case 1:
+
+ /* <item></item> block just ended, so ... */
+ if( !strcmp(el, "item") ) {
+
+ /* add the complete feed item to our feed struct */
+ ctx->feed->items =
+ g_slist_prepend(ctx->feed->items, (gpointer)ctx->curitem);
+
+ /* since it's in the linked list, lose this pointer */
+ ctx->curitem = NULL;
+ }
+
+ break;
+
+ case 2:
+
+ switch(ctx->location) {
+
+ /* We're inside introductory <channel></channel> */
+ case FEED_LOC_RDF_CHANNEL:
+ if( !strcmp(el, "title") ) {
+ FILL(feed->title)
+ } else if( !strcmp(el, "description" ) ) {
+ FILL(feed->description)
+ } else if( !strcmp(el, "dc:language") ) {
+ FILL(feed->language)
+ } else if( !strcmp(el, "dc:creator") ) {
+ FILL(feed->author)
+ } else if( !strcmp(el, "dc:date") ) {
+ feed->date = parseISO8601Date(text);
+ } else if( !strcmp(el, "pubDate") ) {
+ feed->date = parseRFC822Date(text);
+ }
+
+ break;
+
+ /* We're inside an <item></item> */
+ case FEED_LOC_RDF_ITEM:
+ if( ctx->curitem == NULL ) {
+ break;
+ }
+
+ /* decide which field did we just get */
+ if( !strcmp(el, "title") ) {
+ FILL(ctx->curitem->title)
+ } else if( !strcmp(el, "dc:creator") ) {
+ FILL(ctx->curitem->author)
+ } else if( !strcmp(el, "description") ) {
+ FILL(ctx->curitem->summary)
+ } else if( !strcmp(el, "content:encoded") ) {
+ FILL(ctx->curitem->text)
+ } else if( !strcmp(el, "link") ) {
+ FILL(ctx->curitem->url)
+ } else if( !strcmp(el, "dc:date") ) {
+ ctx->curitem->date_modified = parseISO8601Date(text);
+ } else if( !strcmp(el, "pubDate") ) {
+ ctx->curitem->date_modified = parseRFC822Date(text);
+ }
+
+ break;
+ }
+
+ break;
+
+ }
+
+ if( ctx->str != NULL ) {
+ g_string_free(ctx->str, TRUE);
+ ctx->str = NULL;
+ }
+}
diff --git a/src/plugins/rssyl/libfeed/parser_rdf.h b/src/plugins/rssyl/libfeed/parser_rdf.h
new file mode 100644
index 0000000..1c3809f
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_rdf.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __PARSER_RDF_H
+#define __PARSER_RDF_H
+
+void feed_parser_rdf_start(void *data, const char *el, const char **attr);
+void feed_parser_rdf_end(void *data, const char *el);
+
+enum {
+ FEED_LOC_RDF_NONE,
+ FEED_LOC_RDF_CHANNEL,
+ FEED_LOC_RDF_ITEM
+} FeedRdfLocations;
+
+#endif /* __PARSER_RDF_H */
diff --git a/src/plugins/rssyl/libfeed/parser_rss20.c b/src/plugins/rssyl/libfeed/parser_rss20.c
new file mode 100644
index 0000000..3882750
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_rss20.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+#define __USE_GNU
+
+#include <glib.h>
+#include <expat.h>
+#include <string.h>
+
+#include "feed.h"
+#include "feeditem.h"
+#include "feeditemenclosure.h"
+#include "date.h"
+#include "parser.h"
+
+void feed_parser_rss20_start(void *data, const gchar *el, const gchar **attr)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ FeedItemEnclosure *enclosure = NULL;
+ gchar *url, *type, *size_s;
+ gulong size = -1;
+
+ /* ------------------- */
+ if( ctx->depth == 2 ) {
+ if( !strcmp(el, "item") ) { /* Start of new item */
+
+ if( ctx->curitem != NULL )
+ feed_item_free(ctx->curitem);
+
+ ctx->curitem = feed_item_new(ctx->feed);
+
+ } else ctx->location = 0;
+ /* ------------------- */
+ } else if( ctx->depth == 3 ) {
+ if( !strcmp(el, "enclosure") ) { /* Media enclosure */
+
+ url = feed_parser_get_attribute_value(attr, "url");
+ type = feed_parser_get_attribute_value(attr, "type");
+ size_s = feed_parser_get_attribute_value(attr, "length");
+ if( size_s != NULL )
+ size = (gulong)atol(size_s);
+
+ if( url != NULL && type != NULL && size > 0 ) {
+ if( (enclosure = feed_item_enclosure_new(url, type, size)) )
+ feed_item_set_enclosure(ctx->curitem, enclosure);
+ }
+
+ } else if( !strcmp(el, "guid") ) { /* Unique ID */
+ type = feed_parser_get_attribute_value(attr, "isPermaLink");
+ if( type != NULL && !strcmp(type, "false") )
+ feed_item_set_id_permalink(ctx->curitem, TRUE);
+ }
+ } else ctx->location = 0;
+
+ ctx->depth++;
+
+}
+
+void feed_parser_rss20_end(void *data, const gchar *el)
+{
+ FeedParserCtx *ctx = (FeedParserCtx *)data;
+ Feed *feed = ctx->feed;
+ gchar *text = NULL;
+
+ if( ctx->str != NULL )
+ text = ctx->str->str;
+ else
+ text = "";
+
+ ctx->depth--;
+
+ switch( ctx->depth ) {
+
+ /* ------------------- */
+ case 0:
+
+ if( !strcmp(el, "rss") ) {
+ /* we finished parsing the feed */
+ ctx->feed->items = g_slist_reverse(ctx->feed->items);
+ }
+
+ break;
+
+ /* ------------------- */
+ case 1:
+
+ break; /* nothing to do at this depth */
+
+ /* ------------------- */
+ case 2:
+
+ /* decide if we just received </item>, so we can
+ * add a complete item to feed */
+ if( !strcmp(el, "item") ) {
+
+ /* append the complete feed item, if it is valid
+ * "All elements of an item are optional, however at least one
+ * of title or description must be present." */
+ if( ctx->curitem->title != NULL || ctx->curitem->summary != NULL ) {
+ ctx->feed->items =
+ g_slist_prepend(ctx->feed->items, (gpointer)ctx->curitem);
+ }
+
+ /* since it's in the linked list, lose this pointer */
+ ctx->curitem = NULL;
+
+ } else if( !strcmp(el, "title") ) { /* so it wasn't end of item */
+ FILL(feed->title)
+ } else if( !strcmp(el, "description" ) ) {
+ FILL(feed->description)
+ } else if( !strcmp(el, "dc:language") ) {
+ FILL(feed->language)
+ } else if( !strcmp(el, "author") ) {
+ FILL(feed->author)
+ } else if( !strcmp(el, "admin:generatorAgent") ) {
+ FILL(feed->generator)
+ } else if( !strcmp(el, "dc:date") ) {
+ feed->date = parseISO8601Date(text);
+ } else if( !strcmp(el, "pubDate") ) {
+ feed->date = parseRFC822Date(text);
+ }
+
+ break;
+
+ /* ------------------- */
+ case 3:
+
+ if( ctx->curitem == NULL ) {
+ break;
+ }
+
+ /* decide which field did we just get */
+ if( !strcmp(el, "title") ) {
+ FILL(ctx->curitem->title)
+ } else if( !strcmp(el, "author") ) {
+ FILL(ctx->curitem->author)
+ } else if( !strcmp(el, "description") ) {
+ FILL(ctx->curitem->summary)
+ } else if( !strcmp(el, "content:encoded") ) {
+ FILL(ctx->curitem->text)
+ } else if( !strcmp(el, "link") ) {
+ FILL(ctx->curitem->url)
+ } else if( !strcmp(el, "guid") ) {
+ FILL(ctx->curitem->id)
+ } else if( !strcmp(el, "wfw:commentRSS") || !strcmp(el, "wfw:commentRss") ) {
+ FILL(ctx->curitem->comments_url)
+ } else if( !strcmp(el, "dc:date") ) {
+ ctx->curitem->date_modified = parseISO8601Date(text);
+ } else if( !strcmp(el, "pubDate") ) {
+ ctx->curitem->date_modified = parseRFC822Date(text);
+ } else if( !strcmp(el, "dc:creator")) {
+ FILL(ctx->curitem->author)
+ }
+
+ break;
+
+ }
+
+ if( ctx->str != NULL ) {
+ g_string_free(ctx->str, TRUE);
+ ctx->str = NULL;
+ }
+}
diff --git a/src/plugins/rssyl/libfeed/parser_rss20.h b/src/plugins/rssyl/libfeed/parser_rss20.h
new file mode 100644
index 0000000..27cc40c
--- /dev/null
+++ b/src/plugins/rssyl/libfeed/parser_rss20.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+#ifndef __PARSER_RSS20_H
+#define __PARSER_RSS20_H
+
+void feed_parser_rss20_start(void *data, const char *el, const char **attr);
+void feed_parser_rss20_end(void *data, const char *el);
+
+#endif /* __PARSER_RSS20_H */
diff --git a/src/plugins/rssyl/old_feeds.c b/src/plugins/rssyl/old_feeds.c
new file mode 100644
index 0000000..45e6cf6
--- /dev/null
+++ b/src/plugins/rssyl/old_feeds.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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.
+ */
+
+/* Expat parser for old feeds.xml */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <expat.h>
+
+#include <alertpanel.h>
+#include <common/utils.h>
+
+#include "libfeed/parser.h"
+#include "old_feeds.h"
+#include "rssyl.h"
+
+struct _oldrssyl_ctx {
+ GSList *oldfeeds;
+};
+
+static void _elparse_start_oldrssyl(void *data, const gchar *el,
+ const gchar **attr)
+{
+ struct _oldrssyl_ctx *ctx = data;
+ OldRFeed *of;
+ gchar *tmp;
+
+#define GETVAL_STR(name) (g_strdup(feed_parser_get_attribute_value(attr, name)))
+#define GETVAL_INT(name) \
+ ((tmp = feed_parser_get_attribute_value(attr, name)) == NULL ? 0 : \
+ (gint)atoi(tmp))
+
+ if (!strcmp(el, "feed")) {
+ of = g_new0(OldRFeed, 1);
+
+ of->name = GETVAL_STR("name");
+ of->official_name = GETVAL_STR("official_name");
+ of->url = GETVAL_STR("url");
+ of->default_refresh_interval = GETVAL_INT("default_refresh_interval");
+ of->refresh_interval = GETVAL_INT("refresh_interval");
+ of->expired_num = GETVAL_INT("expired_num");
+ of->fetch_comments = GETVAL_INT("fetch_comments");
+ of->fetch_comments_for = GETVAL_INT("fetch_comments_for");
+ of->silent_update = GETVAL_INT("silent_update");
+
+ debug_print("RSSyl: old feeds.xml: Adding '%s' (%s).\n", of->name,
+ of->url);
+
+ ctx->oldfeeds = g_slist_prepend(ctx->oldfeeds, of);
+ }
+
+ return;
+}
+
+static void _elparse_end_oldrssyl(void *data, const gchar *el)
+{
+ return;
+}
+
+GSList *rssyl_old_feed_metadata_parse(gchar *filepath)
+{
+ XML_Parser parser;
+ GSList *oldfeeds = NULL;
+ gchar *contents = NULL;
+ gsize length;
+ GError *error;
+ struct _oldrssyl_ctx *ctx;
+
+ debug_print("RSSyl: Starting to parse old feeds.xml\n");
+
+ /* Read contents of the file into memory */
+ if (!g_file_get_contents(filepath, &contents, &length, &error)) {
+ alertpanel_error(_("Couldn't read contents of old feeds.xml file:\n%s"),
+ error->message);
+ debug_print("RSSyl: Couldn't read contents of feeds.xml\n");
+ g_error_free(error);
+ return NULL;
+ }
+
+ /* Set up expat parser */
+ parser = XML_ParserCreate(NULL);
+
+ ctx = g_new0(struct _oldrssyl_ctx, 1);
+ ctx->oldfeeds = NULL;
+ XML_SetUserData(parser, ctx);
+ XML_SetElementHandler(parser,
+ _elparse_start_oldrssyl,
+ _elparse_end_oldrssyl);
+
+ /* Parse the XML, our output ending up in oldfeeds */
+ XML_Parse(parser, contents, length, 1);
+
+ /* And clean up */
+ XML_ParserFree(parser);
+ g_free(contents);
+ oldfeeds = ctx->oldfeeds;
+ g_free(ctx);
+
+ debug_print("RSSyl: old feeds.xml: added %d items in total\n",
+ g_slist_length(oldfeeds));
+
+ return oldfeeds;
+}
+
+static void _free_old_feed_entry(gpointer d, gpointer user_data)
+{
+ OldRFeed *of = (OldRFeed *)d;
+
+ if (of == NULL)
+ return;
+
+ g_free(of->name);
+ g_free(of->official_name);
+ g_free(of->url);
+ g_free(of);
+}
+
+void rssyl_old_feed_metadata_free(GSList *oldfeeds)
+{
+ if (oldfeeds != NULL) {
+ debug_print("RSSyl: releasing parsed contents of old feeds.xml\n");
+ g_slist_foreach(oldfeeds, _free_old_feed_entry, NULL);
+ g_slist_free(oldfeeds);
+ oldfeeds = NULL;
+ }
+}
+
+static gint _old_feed_find_by_url(gconstpointer a, gconstpointer b)
+{
+ OldRFeed *of = (OldRFeed *)a;
+ gchar *name = (gchar *)b;
+
+ if (of == NULL || of->name == NULL || of->url == NULL || name == NULL)
+ return 1;
+
+ return strcmp(of->name, name);
+}
+
+OldRFeed *rssyl_old_feed_get_by_name(GSList *oldfeeds, gchar *name)
+{
+ GSList *needle;
+
+ g_return_val_if_fail(oldfeeds != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ if ((needle = g_slist_find_custom(oldfeeds, name, _old_feed_find_by_url))
+ != NULL)
+ return (OldRFeed *)needle->data;
+
+ return NULL;
+}
diff --git a/src/plugins/rssyl/old_feeds.h b/src/plugins/rssyl/old_feeds.h
new file mode 100644
index 0000000..b52cd86
--- /dev/null
+++ b/src/plugins/rssyl/old_feeds.h
@@ -0,0 +1,22 @@
+#ifndef __RSSYL_OLD_FEEDS
+#define __RSSYL_OLD_FEEDS
+
+struct _OldRFeed {
+ gchar *name;
+ gchar *official_name;
+ gchar *url;
+ gint default_refresh_interval;
+ gint refresh_interval;
+ gint expired_num;
+ gint fetch_comments;
+ gint fetch_comments_for;
+ gint silent_update;
+};
+
+typedef struct _OldRFeed OldRFeed;
+
+GSList *rssyl_old_feed_metadata_parse(gchar *filepath);
+void rssyl_old_feed_metadata_free(GSList *oldfeeds);
+OldRFeed *rssyl_old_feed_get_by_name(GSList *oldfeeds, gchar *name);
+
+#endif /* __RSSYL_OLD_FEEDS */
diff --git a/src/plugins/rssyl/opml.c b/src/plugins/rssyl/opml_export.c
similarity index 50%
rename from src/plugins/rssyl/opml.c
rename to src/plugins/rssyl/opml_export.c
index c71cf8b..7ec9369 100644
--- a/src/plugins/rssyl/opml.c
+++ b/src/plugins/rssyl/opml_export.c
@@ -23,42 +23,35 @@
# include "config.h"
#endif
-#include <errno.h>
+/* Global includes */
#include <glib.h>
+#include <glib/gi18n.h>
+#include <errno.h>
-#include <libxml/parser.h>
-#include <libxml/xpath.h>
-
-#include "log.h"
-#include "folder.h"
-#include "folderview.h"
+/* Claws Mail includes */
+#include <log.h>
+#include <folder.h>
+#include <common/utils.h>
-#include "date.h"
-#include "feed.h"
+/* Local includes */
+#include "libfeed/date.h"
#include "rssyl.h"
-#include "strreplace.h"
+#include "opml_import.h"
+#include "strutils.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 {
+struct _RSSylOpmlCtx {
FILE *f;
gint depth;
};
-typedef struct _RSSylOpmlExportCtx RSSylOpmlExportCtx;
+typedef struct _RSSylOpmlCtx RSSylOpmlCtx;
static void rssyl_opml_export_func(FolderItem *item, gpointer data)
{
- RSSylOpmlExportCtx *ctx = (RSSylOpmlExportCtx *)data;
- RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+ RSSylOpmlCtx *ctx = (RSSylOpmlCtx *)data;
+ RFolderItem *ritem = (RFolderItem *)item;
gboolean isfolder = FALSE, err = FALSE;
gboolean haschildren = FALSE;
gchar *indent = NULL, *xmlurl = NULL;
@@ -72,10 +65,10 @@ static void rssyl_opml_export_func(FolderItem *item, gpointer data)
return;
/* Check for depth and adjust indentation */
- depth = _folder_depth(item);
+ depth = rssyl_folder_depth(item);
if( depth < ctx->depth ) {
for( ctx->depth--; depth <= ctx->depth; ctx->depth-- ) {
- indent = g_strnfill(ctx->depth, '\t');
+ indent = g_strnfill(ctx->depth + 1, '\t');
err |= (fprintf(ctx->f, "%s</outline>\n", indent) < 0);
g_free(indent);
}
@@ -93,13 +86,13 @@ static void rssyl_opml_export_func(FolderItem *item, gpointer data)
if( g_node_n_children(item->node) )
haschildren = TRUE;
- indent = g_strnfill(ctx->depth, '\t');
+ indent = g_strnfill(ctx->depth + 1, '\t');
tmpname = rssyl_strreplace(item->name, "&", "&");
- if (ritem->official_name != NULL)
- tmpoffn = rssyl_strreplace(item->name, "&", "&");
- else
- tmpoffn = g_strdup(tmpname);
+ if( ritem->official_title != NULL )
+ tmpoffn = rssyl_strreplace(ritem->official_title, "&", "&");
+ else
+ tmpoffn = g_strdup(tmpname);
err |= (fprintf(ctx->f,
"%s<outline title=\"%s\" text=\"%s\" description=\"%s\" type=\"%s\" %s%s>\n",
@@ -114,7 +107,7 @@ static void rssyl_opml_export_func(FolderItem *item, gpointer data)
if( err ) {
log_warning(LOG_PROTOCOL,
- "Error while writing '%s' to feed export list.\n",
+ _("RSSyl: Error while writing '%s' to feed export list.\n"),
item->name);
debug_print("Error while writing '%s' to feed_export list.\n",
item->name);
@@ -124,9 +117,9 @@ static void rssyl_opml_export_func(FolderItem *item, gpointer data)
void rssyl_opml_export(void)
{
FILE *f;
- gchar *opmlfile, *tmpdate, *indent;
+ gchar *opmlfile, *tmp;
time_t tt = time(NULL);
- RSSylOpmlExportCtx *ctx = NULL;
+ RSSylOpmlCtx *ctx = NULL;
gboolean err = FALSE;
opmlfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
@@ -137,14 +130,14 @@ void rssyl_opml_export(void)
if( (f = g_fopen(opmlfile, "w")) == NULL ) {
log_warning(LOG_PROTOCOL,
- "Couldn't open file '%s' for feed list exporting: %s\n",
+ _("RSSyl: 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");
+ debug_print("RSSyl: Couldn't open feed list export file, returning.\n");
g_free(opmlfile);
return;
}
- tmpdate = createRFC822Date(&tt);
+ tmp = createRFC822Date(&tt);
/* Write OPML header */
err |= (fprintf(f,
@@ -155,20 +148,22 @@ void rssyl_opml_export(void)
"\t\t<dateCreated>%s</dateCreated>\n"
"\t</head>\n"
"\t<body>\n",
- tmpdate) < 0);
- g_free(tmpdate);
+ tmp) < 0);
+ g_free(tmp);
- ctx = g_new0(RSSylOpmlExportCtx, 1);
+ ctx = g_new0(RSSylOpmlCtx, 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);
+ /* Print close all open <outline> tags if needed. */
+ while( ctx->depth > 2 ) {
+ ctx->depth--;
+ tmp = g_strnfill(ctx->depth, '\t');
+ err |= (fprintf(f, "%s</outline>\n", tmp) < 0);
+ g_free(tmp);
}
err |= (fprintf(f,
@@ -176,84 +171,13 @@ void rssyl_opml_export(void)
"</opml>\n") < 0);
if( err ) {
- log_warning(LOG_PROTOCOL, "Error during writing feed export file.\n");
- debug_print("Error during writing feed export file.");
+ log_warning(LOG_PROTOCOL, _("RSSyl: Error during writing feed export file.\n"));
+ debug_print("RSSyl: Error during writing feed export file.\n");
}
- debug_print("Feed export finished.\n");
+ debug_print("RSSyl: 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);
-}
diff --git a/src/plugins/rssyl/opml.h b/src/plugins/rssyl/opml_export.h
similarity index 64%
rename from src/plugins/rssyl/opml.h
rename to src/plugins/rssyl/opml_export.h
index 9a8aef2..6e89149 100644
--- a/src/plugins/rssyl/opml.h
+++ b/src/plugins/rssyl/opml_export.h
@@ -4,6 +4,5 @@
#include "rssyl.h"
void rssyl_opml_export(void);
-void rssyl_opml_import(const gchar *opmlfile, FolderItem *parent);
#endif /* __RSSYL_OPML */
diff --git a/src/plugins/rssyl/opml_import.c b/src/plugins/rssyl/opml_import.c
new file mode 100644
index 0000000..2a34f5d
--- /dev/null
+++ b/src/plugins/rssyl/opml_import.c
@@ -0,0 +1,110 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * - OPML import handling
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* Claws Mail includes */
+#include <folder.h>
+#include <alertpanel.h>
+#include <common/utils.h>
+
+/* Local includes */
+#include "rssyl_feed.h"
+#include "opml_import.h"
+
+gint rssyl_folder_depth(FolderItem *item)
+{
+ gint i;
+
+ for( i = -1; item != NULL; item = folder_item_parent(item), i++ ) {}
+ return i;
+}
+
+/* This gets called from the libfeed's OPML parser as a user function for
+ * each <outline ...> from the .opml file.
+ * It creates a folder or subscribes a feed, while keeping track of the
+ * location inside folder hierarchy (using depth and linked list of parent
+ * folders up to the root). */
+void rssyl_opml_import_func(gchar *title, gchar *url, gint depth, gpointer data)
+{
+ OPMLImportCtx *ctx = (OPMLImportCtx *)data;
+ gchar *tmp = NULL;
+ FolderItem *new_item;
+ gboolean nulltitle = FALSE;
+ gint i = 1;
+
+ debug_print("depth %d, ctx->depth %d\n", depth, ctx->depth);
+ while (depth < ctx->depth) {
+ /* We've gone up at least one level, need to find correct parent */
+ ctx->current = g_slist_delete_link(ctx->current, ctx->current);
+ ctx->depth--;
+ }
+
+ debug_print("OPML_IMPORT: %s %s (%s)\n",
+ (url != NULL ? "feed": "folder"), title, url);
+
+ if( title == NULL ) {
+ debug_print("NULL title received, substituting a placeholder title\n");
+ title = g_strdup(_("Untitled"));
+ nulltitle = TRUE;
+ }
+
+ /* If URL is not given, then it's a folder */
+ if( url == NULL ) {
+ /* Find an unused name for new folder */
+ tmp = g_strdup(title);
+ while (folder_find_child_item_by_name((FolderItem *)ctx->current->data, tmp)) {
+ debug_print("RSSyl: Folder '%s' already exists, trying another name\n",
+ title);
+ g_free(tmp);
+ tmp = g_strdup_printf("%s__%d", title, ++i);
+ }
+
+ /* Create the folder */
+ new_item = folder_create_folder((FolderItem *)ctx->current->data, tmp);
+ if (!new_item) {
+ alertpanel_error(_("Can't create the folder '%s'."), tmp);
+ g_free(tmp);
+ }
+
+ if (nulltitle) {
+ g_free(title);
+ title = NULL;
+ }
+
+ ctx->current = g_slist_prepend(ctx->current, new_item);
+ ctx->depth++;
+ } else {
+ /* We have URL, try to add new feed... */
+ new_item = rssyl_feed_subscribe_new((FolderItem *)ctx->current->data,
+ url, TRUE);
+ /* ...and rename it if needed */
+ if (new_item != NULL && strcmp(title, new_item->name))
+ folder_item_rename(new_item, title);
+ }
+}
diff --git a/src/plugins/rssyl/opml_import.h b/src/plugins/rssyl/opml_import.h
new file mode 100644
index 0000000..91b71aa
--- /dev/null
+++ b/src/plugins/rssyl/opml_import.h
@@ -0,0 +1,15 @@
+#ifndef __OPML_IMPORT
+#define __OPML_IMPORT
+
+struct _OPMLImportCtx {
+ GSList *current;
+ gint depth;
+ gint failures;
+};
+
+typedef struct _OPMLImportCtx OPMLImportCtx;
+
+gint rssyl_folder_depth(FolderItem *item);
+void rssyl_opml_import_func(gchar *title, gchar *url, gint depth, gpointer data);
+
+#endif /* __OPML_IMPORT */
diff --git a/src/plugins/rssyl/parse822.c b/src/plugins/rssyl/parse822.c
new file mode 100644
index 0000000..846fb6d
--- /dev/null
+++ b/src/plugins/rssyl/parse822.c
@@ -0,0 +1,352 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * 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
+
+/* Global includes */
+#include <sys/stat.h>
+#include <glib.h>
+#include <pthread.h>
+
+/* Claws Mail includes */
+#include <common/claws.h>
+#include <procheader.h>
+#include <common/utils.h>
+#include <main.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "libfeed/feeditem.h"
+#include "libfeed/date.h"
+#include "parse822.h"
+#include "rssyl_feed.h"
+#include "rssyl_parse_feed.h"
+#include "strutils.h"
+
+/* rssyl_parse_folder_item_file()
+ *
+ * Parse a RFC822-formatted feed item given by "path", and returns a
+ * pointer to a newly-allocated FeedItem struct, which contains all required data.
+ *
+ */
+FeedItem *rssyl_parse_folder_item_file(gchar *path)
+{
+ gchar *contents, **lines, **line, **splid;
+ GError *error = NULL;
+ FeedItem *item;
+ RFeedCtx *ctx;
+ gint i = 0;
+ gboolean parsing_headers = TRUE, past_html_tag = FALSE, past_endhtml_tag = FALSE;
+ gboolean started_author = FALSE, started_subject = FALSE;
+ gboolean started_link = FALSE, started_clink = FALSE, got_original_title = FALSE;
+
+ debug_print("RSSyl: parsing '%s'\n", path);
+
+ g_file_get_contents(path, &contents, NULL, &error);
+
+ if( error )
+ g_warning("GError: '%s'\n", error->message);
+
+ if( contents != NULL ) {
+ lines = strsplit_no_copy(contents, '\n');
+ } else {
+ g_warning("Badly formatted file found, ignoring: '%s'\n", path);
+ return NULL;
+ }
+
+ ctx = g_new0(RFeedCtx, 1);
+ ctx->path = g_strdup(path); /* store filesystem path to source file */
+ ctx->last_seen = 0;
+
+ item = feed_item_new(NULL);
+ item->data = ctx;
+
+ while( lines[i] ) {
+ if( parsing_headers && lines[i] && !strlen(lines[i]) ) {
+ parsing_headers = FALSE;
+ debug_print("RSSyl: finished parsing headers\n");
+ }
+
+ if( parsing_headers ) {
+ line = g_strsplit(lines[i], ": ", 2);
+ if( line[0] && line[1] && strlen(line[0]) && lines[i][0] != ' ') {
+ started_author = FALSE;
+ started_subject = FALSE;
+ started_link = FALSE;
+ started_clink = FALSE;
+
+ /* Author */
+ if( !strcmp(line[0], "From") ) {
+ feed_item_set_author(item, line[1]);
+ debug_print("RSSyl: got author '%s'\n", feed_item_get_author(item));
+ started_author = TRUE;
+ }
+
+ /* Date */
+ if( !strcmp(line[0], "Date") ) {
+ feed_item_set_date_modified(item,
+ procheader_date_parse(NULL, line[1], 0));
+ debug_print("RSSyl: got date \n" );
+ }
+
+ /* Title */
+ if( !strcmp(line[0], "Subject") && !got_original_title ) {
+ feed_item_set_title(item,line[1]);
+ debug_print("RSSyl: got title '%s'\n", feed_item_get_title(item));
+ started_subject = TRUE;
+ }
+
+ /* Original (including HTML) title - Atom feeds */
+ if( !strcmp(line[0], "X-RSSyl-OrigTitle") ) {
+ feed_item_set_title(item, line[1]);
+ debug_print("RSSyl: got original title '%s'\n",
+ feed_item_get_title(item));
+ got_original_title = TRUE;
+ }
+
+ /* URL */
+ if( !strcmp(line[0], "X-RSSyl-URL") ) {
+ feed_item_set_url(item, line[1]);
+ debug_print("RSSyl: got link '%s'\n", feed_item_get_url(item));
+ started_link = TRUE;
+ }
+
+ /* Last-Seen timestamp */
+ if( !strcmp(line[0], "X-RSSyl-Last-Seen") ) {
+ ctx->last_seen = atol(line[1]);
+ debug_print("RSSyl: got last_seen timestamp %ld\n", ctx->last_seen);
+ }
+
+ /* ID */
+ if( !strcmp(line[0], "Message-ID") ) {
+ splid = g_strsplit_set(line[1], "<>", 3);
+ if( strlen(splid[1]) != 0 )
+ feed_item_set_id(item, splid[1]);
+ g_strfreev(splid);
+ }
+
+ /* Feed comments */
+ if( !strcmp(line[0], "X-RSSyl-Comments") ) {
+ feed_item_set_comments_url(item, line[1]);
+ debug_print("RSSyl: got clink '%s'\n", feed_item_get_comments_url(item));
+ started_clink = TRUE;
+ }
+
+ /* References */
+ if( !strcmp(line[0], "References") ) {
+ splid = g_strsplit_set(line[1], "<>", 3);
+ if( strlen(splid[1]) != 0 )
+ feed_item_set_parent_id(item, line[1]);
+ g_strfreev(splid);
+ }
+
+ } else if (lines[i][0] == ' ') {
+ gchar *tmp = NULL;
+ /* continuation line */
+ if (started_author) {
+ tmp = g_strdup_printf("%s %s", feed_item_get_author(item), lines[i]+1);
+ feed_item_set_author(item, tmp);
+ debug_print("RSSyl: updated author to '%s'\n", tmp);
+ g_free(tmp);
+ } else if (started_subject) {
+ tmp = g_strdup_printf("%s %s", feed_item_get_title(item), lines[i]+1);
+ feed_item_set_title(item, tmp);
+ debug_print("RSSyl: updated title to '%s'\n", tmp);
+ g_free(tmp);
+ } else if (started_link) {
+ tmp = g_strdup_printf("%s%s", feed_item_get_url(item), lines[i]+1);
+ feed_item_set_url(item, tmp);
+ debug_print("RSSyl: updated link to '%s'\n", tmp);
+ g_free(tmp);
+ } else if (started_clink) {
+ tmp = g_strdup_printf("%s%s", feed_item_get_comments_url(item), lines[i]+1);
+ feed_item_set_comments_url(item, tmp);
+ debug_print("RSSyl: updated comments_link to '%s'\n", tmp);
+ }
+ }
+ g_strfreev(line);
+ } else {
+ if( !strcmp(lines[i], RSSYL_TEXT_START) ) {
+ debug_print("RSSyl: Leading html tag found at line %d\n", i);
+ past_html_tag = TRUE;
+ i++;
+ continue;
+ }
+ while( past_html_tag && !past_endhtml_tag && lines[i] ) {
+ if( !strcmp(lines[i], RSSYL_TEXT_END) ) {
+ debug_print("RSSyl: Trailing html tag found at line %d\n", i);
+ past_endhtml_tag = TRUE;
+ i++;
+ continue;
+ }
+ if( feed_item_get_text(item) != NULL ) {
+ gint e_len, n_len;
+ e_len = strlen(item->text);
+ n_len = strlen(lines[i]);
+ item->text = g_realloc(item->text, e_len + n_len + 2);
+ *(item->text+e_len) = '\n';
+ strcpy(item->text+e_len+1, lines[i]);
+ *(item->text+e_len+n_len+1) = '\0';
+ } else {
+ item->text = g_strdup(lines[i]);
+ }
+ i++;
+ }
+
+ if( lines[i] == NULL )
+ return item;
+ }
+
+ i++;
+ }
+ g_free(lines);
+ g_free(contents);
+ return item;
+}
+
+static void rssyl_flush_folder_func(gpointer data, gpointer user_data)
+{
+ FeedItem *item = (FeedItem *)data;
+ RFeedCtx *ctx = (RFeedCtx *)item->data;
+
+ if( ctx != NULL && ctx->path != NULL)
+ g_free(ctx->path);
+ feed_item_free(item);
+}
+
+static void rssyl_folder_read_existing_real(RFolderItem *ritem)
+{
+ gchar *path = NULL, *fname = NULL;
+ DIR *dp;
+ struct dirent *d;
+ struct stat st;
+ gint num;
+ FeedItem *item = NULL;
+ RFeedCtx *ctx;
+
+ g_return_if_fail(ritem != NULL);
+
+ path = folder_item_get_path(&ritem->item);
+ g_return_if_fail(path != NULL);
+
+ debug_print("RSSyl: reading existing items from '%s'\n", path);
+
+ /* Flush contents if any, so we can add new */
+ if( g_slist_length(ritem->items) > 0 ) {
+ g_slist_foreach(ritem->items, (GFunc)rssyl_flush_folder_func, NULL);
+ g_slist_free(ritem->items);
+ }
+ ritem->items = NULL;
+ ritem->last_update = 0;
+
+ if( (dp = opendir(path)) == NULL ) {
+ FILE_OP_ERROR(path, "opendir");
+ g_free(path);
+ return;
+ }
+
+ while( (d = readdir(dp)) != NULL ) {
+ if( claws_is_exiting() ) {
+ closedir(dp);
+ g_free(path);
+ return;
+ }
+
+ if( d->d_name[0] != '.' && (num = to_number(d->d_name)) > 0 ) {
+ fname = g_strdup_printf("%s%c%s", path, G_DIR_SEPARATOR, d->d_name);
+ if( g_stat(fname, &st) < 0 ) {
+ debug_print("RSSyl: couldn't stat() file '%s', ignoring it\n", fname);
+ g_free(fname);
+ continue;
+ }
+
+ if( !S_ISREG(st.st_mode)) {
+ debug_print("RSSyl: not a regular file: '%s', ignoring it\n", fname);
+ g_free(fname);
+ continue;
+ }
+
+ debug_print("RSSyl: starting to parse '%s'\n", d->d_name);
+ if( (item = rssyl_parse_folder_item_file(fname)) != NULL ) {
+ /* Find latest timestamp */
+ ctx = (RFeedCtx *)item->data;
+ if( ritem->last_update < ctx->last_seen )
+ ritem->last_update = ctx->last_seen;
+ debug_print("RSSyl: Appending '%s'\n", feed_item_get_title(item));
+ ritem->items = g_slist_prepend(ritem->items, item);
+ }
+ g_free(fname);
+ }
+ }
+
+ closedir(dp);
+ g_free(path);
+
+ ritem->items = g_slist_reverse(ritem->items);
+}
+
+#ifdef USE_PTHREAD
+static void *rssyl_read_existing_thr(void *arg)
+{
+ RParseCtx *ctx = (RParseCtx *)arg;
+
+ rssyl_folder_read_existing_real(ctx->ritem);
+ ctx->ready = TRUE;
+ return NULL;
+}
+#endif
+
+void rssyl_folder_read_existing(RFolderItem *ritem)
+{
+#ifdef USE_PTHREAD
+ RParseCtx *ctx;
+ pthread_t pt;
+#endif
+
+ g_return_if_fail(ritem != NULL);
+
+
+#ifdef USE_PTHREAD
+ ctx = g_new0(RParseCtx, 1);
+ ctx->ritem = ritem;
+ ctx->ready = FALSE;
+
+ if( pthread_create(&pt, PTHREAD_CREATE_JOINABLE, rssyl_read_existing_thr,
+ (void *)ctx) != 0 ) {
+ /* Couldn't create thread, let's continue non-threaded. */
+ rssyl_folder_read_existing_real(ritem);
+ } else {
+ /* Thread started, wait until it is done. */
+ debug_print("RSSyl: waiting for thread to finish\n");
+ while( !ctx->ready ) {
+ claws_do_idle();
+ }
+
+ debug_print("RSSyl: thread finished\n");
+ pthread_join(pt, NULL);
+ }
+
+ g_free(ctx);
+#else
+ rssyl_folder_read_existing_real(ritem);
+#endif
+}
diff --git a/src/plugins/rssyl/parse822.h b/src/plugins/rssyl/parse822.h
new file mode 100644
index 0000000..94318fc
--- /dev/null
+++ b/src/plugins/rssyl/parse822.h
@@ -0,0 +1,19 @@
+#ifndef __RSSYL_PARSE822_H
+#define __RSSYL_PARSE822_H
+
+#include <glib.h>
+
+#include "libfeed/feeditem.h"
+#include "rssyl.h"
+
+struct _RFeedCtx {
+ gchar *path;
+ time_t last_seen;
+};
+
+typedef struct _RFeedCtx RFeedCtx;
+
+FeedItem *rssyl_parse_folder_item_file(gchar *path);
+void rssyl_folder_read_existing(RFolderItem *ritem);
+
+#endif /* __RSSYL_PARSE822_H */
diff --git a/src/plugins/rssyl/parsers.c b/src/plugins/rssyl/parsers.c
deleted file mode 100644
index bc491ae..0000000
--- a/src/plugins/rssyl/parsers.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- * 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") &&
- (n->ns && n->ns->prefix && (!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") &&
- (n->ns && n->ns->prefix && !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") &&
- (!n->ns || !n->ns->prefix || !strlen(n->ns->prefix)) ) {
- 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;
-}
diff --git a/src/plugins/rssyl/parsers.h b/src/plugins/rssyl/parsers.h
deleted file mode 100644
index 48321e6..0000000
--- a/src/plugins/rssyl/parsers.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#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 */
diff --git a/src/plugins/rssyl/plugin.c b/src/plugins/rssyl/plugin.c
index 9cf3cdc..7ade914 100644
--- a/src/plugins/rssyl/plugin.c
+++ b/src/plugins/rssyl/plugin.c
@@ -1,5 +1,5 @@
/*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Claws-Mail-- 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>
*
@@ -22,23 +22,25 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
-#include "claws-features.h"
+# include "claws-features.h"
#endif
-#include <glib.h>
+/* Global includes */
#include <glib/gi18n.h>
-
-#include "common/version.h"
-#include "claws.h"
#include <curl/curl.h>
+/* Claws Mail includes */
+#include <common/claws.h>
+#include <common/version.h>
+#include <plugin.h>
+
+/* Local includes */
#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) )
+ if( !check_plugin_version(MAKE_NUMERIC_VERSION(3, 7, 8, 31),
+ VERSION_NUMERIC, "RSSyl", error) )
return -1;
curl_global_init(CURL_GLOBAL_DEFAULT);
@@ -55,7 +57,7 @@ gboolean plugin_done(void)
const gchar *plugin_name(void)
{
- return PLUGIN_NAME;
+ return "RSSyl";
}
const gchar *plugin_desc(void)
diff --git a/src/plugins/rssyl/rssyl.c b/src/plugins/rssyl/rssyl.c
index 670dd86..2c5a634 100644
--- a/src/plugins/rssyl/rssyl.c
+++ b/src/plugins/rssyl/rssyl.c
@@ -1,5 +1,5 @@
/*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Claws-Mail-- 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>
*
@@ -22,52 +22,73 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
-#include "claws-features.h"
#endif
+/* Global includes */
#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"
+/* Claws Mail includes */
+#include <folder.h>
+#include <procmsg.h>
+#include <localfolder.h>
+#include <common/utils.h>
+#include <main.h>
+#include <mh.h>
+#include <xml.h>
+#include <toolbar.h>
+#include <prefs_toolbar.h>
+
+/* Local includes */
+#include "libfeed/feeditem.h"
#include "rssyl.h"
+#include "rssyl_deleted.h"
#include "rssyl_gtk.h"
+#include "rssyl_feed.h"
#include "rssyl_prefs.h"
-#include "strreplace.h"
+#include "rssyl_update_feed.h"
+#include "rssyl_update_format.h"
+#include "opml_import.h"
+#include "opml_export.h"
+#include "strutils.h"
+
+FolderClass rssyl_class;
static gint rssyl_create_tree(Folder *folder);
+static gint rssyl_scan_tree(Folder *folder);
static gboolean existing_tree_found = FALSE;
static void rssyl_init_read_func(FolderItem *item, gpointer data)
{
+ RFolderItem *ritem = (RFolderItem *)item;
+ RPrefs *rsprefs = NULL;
+
if( !IS_RSSYL_FOLDER_ITEM(item) )
return;
existing_tree_found = TRUE;
- if( folder_item_parent(item) == NULL )
+ /* Don't do anything if we're on root of our folder tree or on
+ * a regular folder (no feed) */
+ if( folder_item_parent(item) == NULL || ritem->url == NULL )
return;
- rssyl_get_feed_props((RSSylFolderItem *)item);
+ ritem->refresh_id = 0;
+
+ /* Start automatic refresh timer, if necessary */
+ if( ritem->default_refresh_interval ) {
+ rsprefs = rssyl_prefs_get();
+ if( !rsprefs->refresh_enabled )
+ return;
+
+ ritem->refresh_interval = rsprefs->refresh;
+ }
+
+ /* Start the timer, if determined interval is >0 */
+ if( ritem->refresh_interval > 0 )
+ rssyl_feed_start_refresh_timeout(ritem);
}
static void rssyl_make_rc_dir(void)
@@ -80,7 +101,7 @@ static void rssyl_make_rc_dir(void)
g_warning("couldn't create directory %s\n", rssyl_dir);
}
- debug_print("created directorty %s\n", rssyl_dir);
+ debug_print("RSSyl: created directory %s\n", rssyl_dir);
}
g_free(rssyl_dir);
@@ -89,7 +110,6 @@ static void rssyl_make_rc_dir(void)
static void rssyl_create_default_mailbox(void)
{
Folder *root = NULL;
- FolderItem *item;
rssyl_make_rc_dir();
@@ -98,20 +118,21 @@ static void rssyl_create_default_mailbox(void)
g_return_if_fail(root != NULL);
folder_add(root);
- item = FOLDER_ITEM(root->node->data);
+ rssyl_scan_tree(root);
- rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
+ /* FIXME: subscribe default feed */
+// rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
}
-static gboolean rssyl_refresh_all_feeds_deferred(gpointer data)
+static gboolean rssyl_update_all_feeds_deferred(gpointer data)
{
- rssyl_refresh_all_feeds();
+ rssyl_update_all_feeds();
return FALSE;
}
-static void rssyl_toolbar_cb_refresh_all(gpointer parent, const gchar *item_name, gpointer data)
+static void rssyl_toolbar_cb_refresh_all_feeds(gpointer parent, const gchar *item_name, gpointer data)
{
- rssyl_refresh_all_feeds();
+ rssyl_update_all_feeds();
}
void rssyl_init(void)
@@ -119,34 +140,37 @@ 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 )
+ if( !existing_tree_found )
rssyl_create_default_mailbox();
+ else
+ rssyl_update_format();
- prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl",
- _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all, NULL);
-
- rssyl_opml_export();
+ prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl", _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all_feeds, NULL);
if( rssyl_prefs_get()->refresh_on_startup &&
claws_is_starting() )
- g_timeout_add(2000, rssyl_refresh_all_feeds_deferred, NULL);
+ g_timeout_add(2000, rssyl_update_all_feeds_deferred, NULL);
}
void rssyl_done(void)
{
- prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl",
- _("Refresh all feeds"));
+ rssyl_opml_export();
+
+ prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl", _("Refresh all feeds"));
+
rssyl_prefs_done();
rssyl_gtk_done();
- if (!claws_is_exiting())
+
+ if( !claws_is_exiting() )
folder_unregister_class(rssyl_folder_get_class());
+
+ debug_print("RSSyl is done\n");
}
static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
@@ -188,17 +212,15 @@ static void rssyl_get_last_num(Folder *folder, FolderItem *item)
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 ) {
+ if( (dp = opendir(path)) == NULL ) {
FILE_OP_ERROR(item->path, "opendir");
+ g_free(path);
return;
}
+ g_free(path);
+
while( (d = readdir(dp)) != NULL ) {
if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
if( max < num )
@@ -211,36 +233,127 @@ static void rssyl_get_last_num(Folder *folder, FolderItem *item)
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;
+ Folder *folder;
- debug_print("RSSyl: new_folder\n");
+ debug_print("RSSyl: new_folder: %s (%s)\n", name, path);
rssyl_make_rc_dir();
- folder = g_new0(RSSylFolder, 1);
+ folder = g_new0(Folder, 1);
FOLDER(folder)->klass = &rssyl_class;
folder_init(FOLDER(folder), name);
return FOLDER(folder);
}
-static void rssyl_destroy_folder(Folder *_folder)
+static void rssyl_destroy_folder(Folder *folder)
{
- RSSylFolder *folder = (RSSylFolder *)_folder;
-
folder_local_folder_destroy(LOCAL_FOLDER(folder));
}
+static void rssyl_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
+{
+ GList *cur;
+ RFolderItem *ritem = (RFolderItem *)item;
+
+ folder_item_set_xml(folder, item, tag);
+
+ for( cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
+ XMLAttr *attr = (XMLAttr *) cur->data;
+
+ if( !attr || !attr->name || !attr->value)
+ continue;
+
+ /* (str) URL */
+ if( !strcmp(attr->name, "uri")) {
+ g_free(ritem->url);
+ ritem->url = g_strdup(attr->value);
+ }
+ /* (str) Official title */
+ if( !strcmp(attr->name, "official_title")) {
+ g_free(ritem->official_title);
+ ritem->official_title = g_strdup(attr->value);
+ }
+ /* (bool) Keep old items */
+ if( !strcmp(attr->name, "keep_old"))
+ ritem->keep_old = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ /* (bool) Use default refresh_interval */
+ if( !strcmp(attr->name, "default_refresh_interval"))
+ ritem->default_refresh_interval = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ /* (int) Refresh interval */
+ if( !strcmp(attr->name, "refresh_interval"))
+ ritem->refresh_interval = atoi(attr->value);
+ /* (bool) Fetch comments */
+ if( !strcmp(attr->name, "fetch_comments"))
+ ritem->fetch_comments = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ /* (int) Max age of posts to fetch comments for */
+ if( !strcmp(attr->name, "fetch_comments_max_age"))
+ ritem->fetch_comments_max_age = atoi(attr->value);
+ /* (bool) Write heading */
+ if( !strcmp(attr->name, "write_heading"))
+ ritem->write_heading = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ /* (int) Silent update */
+ if( !strcmp(attr->name, "silent_update"))
+ ritem->silent_update = atoi(attr->value);
+ /* (bool) Ignore title rename */
+ if( !strcmp(attr->name, "ignore_title_rename"))
+ ritem->ignore_title_rename = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ /* (bool) Verify SSL peer */
+ if( !strcmp(attr->name, "ssl_verify_peer"))
+ ritem->ssl_verify_peer = (atoi(attr->value) == 0 ? FALSE : TRUE );
+ }
+}
+
+static XMLTag *rssyl_item_get_xml(Folder *folder, FolderItem *item)
+{
+ XMLTag *tag;
+ RFolderItem *ri = (RFolderItem *)item;
+ gchar *tmp = NULL;
+
+ tag = folder_item_get_xml(folder, item);
+
+ /* (str) URL */
+ if( ri->url != NULL )
+ xml_tag_add_attr(tag, xml_attr_new("uri", ri->url));
+ /* (str) Official title */
+ if( ri->official_title != NULL )
+ xml_tag_add_attr(tag, xml_attr_new("official_title", ri->official_title));
+ /* (bool) Keep old items */
+ xml_tag_add_attr(tag, xml_attr_new("keep_old",
+ (ri->keep_old ? "1" : "0")) );
+ /* (bool) Use default refresh interval */
+ xml_tag_add_attr(tag, xml_attr_new("default_refresh_interval",
+ (ri->default_refresh_interval ? "1" : "0")) );
+ /* (int) Refresh interval */
+ tmp = g_strdup_printf("%d", ri->refresh_interval);
+ xml_tag_add_attr(tag, xml_attr_new("refresh_interval", tmp));
+ g_free(tmp);
+ /* (bool) Fetch comments */
+ xml_tag_add_attr(tag, xml_attr_new("fetch_comments",
+ (ri->fetch_comments ? "1" : "0")) );
+ /* (int) Max age of posts to fetch comments for */
+ tmp = g_strdup_printf("%d", ri->fetch_comments_max_age);
+ xml_tag_add_attr(tag, xml_attr_new("fetch_comments_max_age", tmp));
+ g_free(tmp);
+ /* (bool) Write heading */
+ xml_tag_add_attr(tag, xml_attr_new("write_heading",
+ (ri->write_heading ? "1" : "0")) );
+ /* (int) Silent update */
+ tmp = g_strdup_printf("%d", ri->silent_update);
+ xml_tag_add_attr(tag, xml_attr_new("silent_update", tmp));
+ g_free(tmp);
+ /* (bool) Ignore title rename */
+ xml_tag_add_attr(tag, xml_attr_new("ignore_title_rename",
+ (ri->ignore_title_rename ? "1" : "0")) );
+ /* (bool) Verify SSL peer */
+ xml_tag_add_attr(tag, xml_attr_new("ssl_verify_peer",
+ (ri->ssl_verify_peer ? "1" : "0")) );
+
+ return tag;
+}
+
static gint rssyl_scan_tree(Folder *folder)
{
g_return_val_if_fail(folder != NULL, -1);
@@ -261,6 +374,8 @@ static gint rssyl_create_tree(Folder *folder)
FolderItem *rootitem;
GNode *rootnode;
+ g_return_val_if_fail(folder != NULL, -1);
+
rssyl_make_rc_dir();
if( !folder->node ) {
@@ -269,9 +384,6 @@ static gint rssyl_create_tree(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");
@@ -280,125 +392,201 @@ static gint rssyl_create_tree(Folder *folder)
static FolderItem *rssyl_item_new(Folder *folder)
{
- RSSylFolderItem *ritem;
-
- debug_print("RSSyl: item_new\n");
-
- ritem = g_new0(RSSylFolderItem, 1);
+ RFolderItem *ritem = g_new0(RFolderItem, 1);
ritem->url = NULL;
+ ritem->official_title = NULL;
+ ritem->source_id = NULL;
+ ritem->items = NULL;
+ ritem->keep_old = FALSE;
ritem->default_refresh_interval = TRUE;
- ritem->default_expired_num = TRUE;
+ ritem->refresh_interval = atoi(PREF_DEFAULT_REFRESH);
ritem->fetch_comments = FALSE;
- ritem->fetch_comments_for = -1;
+ ritem->fetch_comments_max_age = -1;
+ ritem->write_heading = TRUE;
+ ritem->fetching_comments = FALSE;
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->ssl_verify_peer = rssyl_prefs_get()->ssl_verify_peer;
-
- ritem->contents = NULL;
- ritem->feedprop = NULL;
+ ritem->last_update = 0;
+ ritem->ignore_title_rename = FALSE;
return (FolderItem *)ritem;
}
static void rssyl_item_destroy(Folder *folder, FolderItem *item)
{
- RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+ RFolderItem *ritem = (RFolderItem *)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(ritem->official_title);
+ g_slist_free(ritem->items);
- g_free(item);
+ /* Remove a scheduled refresh, if any */
+ if( ritem->refresh_id != 0)
+ g_source_remove(ritem->refresh_id);
+
+ g_free(ritem);
}
static FolderItem *rssyl_create_folder(Folder *folder,
FolderItem *parent, const gchar *name)
{
- gchar *path = NULL, *tmp;
+ gchar *path = NULL, *basepath = NULL, *itempath = NULL;
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);
+
+ path = folder_item_get_path(parent);
+ if( !is_dir_exist(path) ) {
+ if( (make_dir_hier(path) != 0) ) {
+ debug_print("RSSyl: Couldn't create directory (rec) '%s'\n", path);
+ return NULL;
+ }
+ }
+
+ basepath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
+ path = g_strconcat(path, G_DIR_SEPARATOR_S, basepath, NULL);
+
+ if( make_dir(path) < 0 ) {
+ debug_print("RSSyl: Couldn't create directory '%s'\n", path);
+ g_free(path);
+ g_free(basepath);
+ return NULL;
+ }
g_free(path);
+ itempath = g_strconcat((parent->path ? parent->path : ""),
+ G_DIR_SEPARATOR_S, basepath, NULL);
+ newitem = folder_item_new(folder, name, itempath);
+ g_free(itempath);
+ g_free(basepath);
+
+ folder_item_append(parent, newitem);
+
return newitem;
}
+FolderItem *rssyl_get_root_folderitem(FolderItem *item)
+{
+ FolderItem *i;
+
+ for( i = item; folder_item_parent(i) != NULL; i = folder_item_parent(i) ) { }
+ return i;
+}
+
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;
+ gchar *path, *name;
+
+ g_return_val_if_fail(folder != NULL, NULL);
+ g_return_val_if_fail(item != NULL, NULL);
+
+ debug_print("RSSyl: item_get_path\n");
+
+ name = folder_item_get_name(rssyl_get_root_folderitem(item));
+ path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+ G_DIR_SEPARATOR_S, name, G_DIR_SEPARATOR_S, item->path, NULL);
+ g_free(name);
+
+ return path;
+}
+
+static gboolean rssyl_rename_folder_func(GNode *node, gpointer data)
+{
+ FolderItem *item = node->data;
+ gchar **paths = data;
+ const gchar *oldpath = paths[0];
+ const gchar *newpath = paths[1];
+ gchar *base;
+ gchar *new_itempath;
+ gint oldpathlen;
+
+ oldpathlen = strlen(oldpath);
+ if (strncmp(oldpath, item->path, oldpathlen) != 0) {
+ g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
+ return TRUE;
+ }
+
+ base = item->path + oldpathlen;
+ while (*base == G_DIR_SEPARATOR) base++;
+ if (*base == '\0')
+ new_itempath = g_strdup(newpath);
+ else
+ new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
+ NULL);
+ g_free(item->path);
+ item->path = new_itempath;
+
+ return FALSE;
}
static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
const gchar *name)
{
- gchar *oldname = NULL, *oldpath = NULL, *newpath = NULL;
- RSSylFolderItem *ritem = NULL;
+ gchar *oldpath;
+ gchar *dirname;
+ gchar *newpath, *utf8newpath;
+ gchar *basenewpath;
+ gchar *paths[2];
+
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);
+ debug_print("RSSyl: rssyl_rename_folder '%s' -> '%s'\n",
+ item->name, 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) {
+ if (!strcmp(item->name, name))
+ return 0;
+
+ oldpath = folder_item_get_path(item);
+ if( !is_dir_exist(oldpath) )
+ make_dir_hier(oldpath);
+
+ dirname = g_path_get_dirname(oldpath);
+ basenewpath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
+ newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, basenewpath, NULL);
+ g_free(basenewpath);
+
+ 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(oldpath);
+ g_free(newpath);
+
+ if( strchr(item->path, G_DIR_SEPARATOR) != NULL ) {
+ dirname = g_path_get_dirname(item->path);
+ utf8newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
+ g_free(dirname);
+ } else
+ utf8newpath = g_strdup(name);
+
g_free(item->name);
item->name = g_strdup(name);
-
- folder_write_list();
+
+ paths[0] = g_strdup(item->path);
+ paths[1] = utf8newpath;
+ g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ rssyl_rename_folder_func, paths);
+
+ g_free(paths[0]);
+ g_free(paths[1]);
return 0;
}
static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
{
+ gchar *path = 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);
@@ -406,6 +594,14 @@ static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
debug_print("RSSyl: removing folder item %s\n", item->path);
+ path = folder_item_get_path(item);
+ if( remove_dir_recursive(path) < 0 ) {
+ g_warning("can't remove directory '%s'\n", path);
+ g_free(path);
+ return -1;
+ }
+
+ g_free(path);
folder_item_remove(item);
return 0;
@@ -418,40 +614,35 @@ static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
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;
+ debug_print("RSSyl: get_num_list: scanning '%s'\n", item->path);
*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 ) {
+ if( (dp = opendir(path)) == NULL ) {
FILE_OP_ERROR(item->path, "opendir");
+ g_free(path);
return -1;
}
+ g_free(path);
+
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);
+ debug_print("Rssyl: get_num_list: returning %d\n", nummsgs);
+
return nummsgs;
}
@@ -462,15 +653,22 @@ static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
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);
+ gchar *path;
+ gchar *file;
+
+ g_return_val_if_fail(item != NULL, NULL);
+ g_return_val_if_fail(num > 0, NULL);
+
+ path = folder_item_get_path(item);
+ file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
+ g_free(path);
+
+ debug_print("RSSyl: fetch_msg '%s'\n", file);
- g_free(snum);
+ if( !is_file_exist(file)) {
+ g_free(file);
+ return NULL;
+ }
return file;
}
@@ -481,25 +679,24 @@ static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
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);
+ debug_print("RSSyl: get_msginfo: %d\n", num);
+
file = rssyl_fetch_msg(folder, item, num);
g_return_val_if_fail(file != NULL, NULL);
- flags.perm_flags = MSG_NEW | MSG_UNREAD;
+ flags.perm_flags = 0;
flags.tmp_flags = 0;
- msginfo = rssyl_parse_feed_item_to_msginfo(file, flags, TRUE, TRUE, item);
+ msginfo = rssyl_feed_parse_item_to_msginfo(file, flags, TRUE, TRUE, item);
+ g_free(file);
if( msginfo )
msginfo->msgnum = num;
- g_free(file);
-
return msginfo;
}
@@ -523,30 +720,31 @@ static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
destfile = rssyl_get_new_msg_filename(dest);
g_return_val_if_fail(destfile != NULL, -1);
+ debug_print("RSSyl: add_msgs: new filename is '%s'\n", destfile);
-#ifdef G_OS_UNIX
- if( link(fileinfo->file, destfile) < 0 )
-#endif
+ if( link(fileinfo->file, destfile) < 0 ) {
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_hash_table_insert(relation, fileinfo->msginfo != NULL ?
+ (gpointer) fileinfo->msginfo : (gpointer) 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;
@@ -558,28 +756,9 @@ static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
file_list.data = &fileinfo;
file_list.next = NULL;
- ret = rssyl_add_msgs(folder, dest, &file_list, NULL);
- return ret;
+ return rssyl_add_msgs(folder, dest, &file_list, NULL);
}
-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;
@@ -595,13 +774,16 @@ static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
/* are we doing a folder move ? */
tmp = g_strdup_printf("%s.tmp", file);
if (is_file_exist(tmp)) {
- claws_unlink(tmp);
+ g_unlink(tmp);
g_free(tmp);
g_free(file);
return 0;
}
g_free(tmp);
- if( claws_unlink(file) < 0 ) {
+
+ rssyl_deleted_add((RFolderItem *)item, file);
+
+ if( g_unlink(file) < 0 ) {
FILE_OP_ERROR(file, "unlink");
g_free(file);
return -1;
@@ -618,11 +800,29 @@ 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;
+ return (rssyl_feed_subscribe_new(FOLDER_ITEM(folder->node->data), uri, FALSE) ?
+ TRUE : FALSE);
+}
+
+static void rssyl_copy_private_data(Folder *folder, FolderItem *oldi,
+ FolderItem *newi)
+{
+ RFolderItem *olditem = (RFolderItem *)oldi,
+ *newitem = (RFolderItem *)newi;
+
+ g_return_if_fail(folder != NULL);
+ g_return_if_fail(olditem != NULL);
+ g_return_if_fail(newitem != NULL);
+
+ if( olditem->url != NULL ) {
+ g_free(newitem->url);
+ newitem->url = g_strdup(olditem->url);
+ }
+
+ if( olditem->official_title != NULL ) {
+ g_free(newitem->official_title);
+ newitem->official_title = g_strdup(olditem->official_title);
+ }
}
/************************************************************************/
@@ -651,11 +851,14 @@ FolderClass *rssyl_folder_get_class()
rssyl_class.remove_folder = rssyl_remove_folder;
rssyl_class.get_num_list = rssyl_get_num_list;
rssyl_class.scan_required = rssyl_scan_required;
+ rssyl_class.item_set_xml = rssyl_item_set_xml;
+ rssyl_class.item_get_xml = rssyl_item_get_xml;
/* 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.copy_msg = mh_get_class()->copy_msg;
+ rssyl_class.copy_msgs = mh_get_class()->copy_msgs;
rssyl_class.add_msg = rssyl_add_msg;
rssyl_class.add_msgs = rssyl_add_msgs;
rssyl_class.remove_msg = rssyl_remove_msg;
@@ -663,7 +866,8 @@ FolderClass *rssyl_folder_get_class()
// rssyl_class.change_flags = rssyl_change_flags;
rssyl_class.change_flags = NULL;
rssyl_class.subscribe = rssyl_subscribe_uri;
- debug_print("RSSyl: registered folderclass\n");
+ rssyl_class.copy_private_data = rssyl_copy_private_data;
+ rssyl_class.search_msgs = folder_item_search_msgs_local;
}
return &rssyl_class;
diff --git a/src/plugins/rssyl/rssyl.h b/src/plugins/rssyl/rssyl.h
index 917adbe..7b1b169 100644
--- a/src/plugins/rssyl/rssyl.h
+++ b/src/plugins/rssyl/rssyl.h
@@ -5,10 +5,13 @@
#include <folder.h>
-#define PLUGIN_NAME (_("RSSyl"))
+#include "libfeed/feed.h"
/* Name of directory in rcdir where RSSyl will store its data. */
-#define RSSYL_DIR "RSSyl"
+#define RSSYL_DIR "RSSyl"
+
+/* Folder name for a new feed, before it is parsed for the first time. */
+#define RSSYL_NEW_FOLDER_NAME "NewFeed"
/* Default RSSyl mailbox name */
#define RSSYL_DEFAULT_MAILBOX _("My Feeds")
@@ -16,43 +19,80 @@
/* 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;
+/* File where info about user-deleted feed items is stored */
+#define RSSYL_DELETED_FILE ".deleted"
+struct _RFolderItem {
+ FolderItem item;
gchar *url;
- gchar *official_name;
+ gchar *official_title;
+ gchar *source_id;
+
+ gboolean keep_old;
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 fetch_comments_max_age;
+
gint silent_update;
+ gboolean write_heading;
+ gboolean ignore_title_rename;
gboolean ssl_verify_peer;
- struct _RSSylFeedProp *feedprop;
+ guint refresh_id;
+ gboolean fetching_comments;
+ time_t last_update;
+
+ struct _RFeedProp *feedprop;
+
+ GSList *items;
+ GSList *deleted_items;
};
-typedef struct _RSSylFolderItem RSSylFolderItem;
+typedef struct _RFolderItem RFolderItem;
-struct _RSSylRefreshCtx {
- RSSylFolderItem *ritem;
+struct _RRefreshCtx {
+ RFolderItem *ritem;
guint id;
};
-typedef struct _RSSylRefreshCtx RSSylRefreshCtx;
+typedef struct _RRefreshCtx RRefreshCtx;
+
+struct _RFetchCtx {
+ Feed *feed;
+ guint response_code;
+ gchar *error;
+ gboolean success;
+ gboolean ready;
+};
+
+typedef struct _RFetchCtx RFetchCtx;
+
+struct _RParseCtx {
+ RFolderItem *ritem;
+ gboolean ready;
+};
+
+typedef struct _RParseCtx RParseCtx;
+
+struct _RDeletedItem {
+ gchar *id;
+ gchar *title;
+ time_t date_published;
+ time_t date_modified;
+};
+
+typedef struct _RDeletedItem RDeletedItem;
void rssyl_init(void);
void rssyl_done(void);
FolderClass *rssyl_folder_get_class(void);
+FolderItem *rssyl_get_root_folderitem(FolderItem *item);
+
#define IS_RSSYL_FOLDER_ITEM(item) \
(item->folder->klass == rssyl_folder_get_class())
diff --git a/src/plugins/rssyl/rssyl_add_item.c b/src/plugins/rssyl/rssyl_add_item.c
new file mode 100644
index 0000000..b058660
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_add_item.c
@@ -0,0 +1,540 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * - DESCRIPTION HERE
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+/* Claws Mail includes */
+#include <codeconv.h>
+#include <procmsg.h>
+#include <common/utils.h>
+
+/* Local includes */
+#include "libfeed/date.h"
+#include "libfeed/feeditem.h"
+#include "parse822.h"
+#include "rssyl.h"
+#include "rssyl_deleted.h"
+#include "rssyl_feed.h"
+#include "rssyl_parse_feed.h"
+#include "strutils.h"
+
+/* rssyl_cb_feed_compare()
+ *
+ * GCompareFunc function called by glib2's g_slist_find_custom().
+ */
+
+static gint rssyl_cb_feed_compare(const FeedItem *a, const FeedItem *b)
+{
+ gboolean date_eq = FALSE, url_eq = FALSE, title_eq = FALSE;
+ gboolean no_url = FALSE, no_date = FALSE, no_title = FALSE;
+ gchar *atit = NULL, *btit = NULL;
+
+ g_return_val_if_fail(a != NULL && b != NULL, 1);
+
+ /* ID should be unique. If it matches, we've found what we came for. */
+ if( (a->id != NULL) && (b->id != NULL) ) {
+ if( !strcmp(a->id, b->id) ) {
+ return 0;
+ }
+
+ /* If both IDs are present, but they do not match, these are not the
+ * droids we're looking for. */
+ return 1;
+ }
+
+ /* Ok, we have no ID to aid us. Let's have a look at item timestamps
+ * and item title & url. */
+ if( (a->url != NULL) && (b->url != NULL) ) {
+ if( !strcmp(a->url, b->url) )
+ url_eq = TRUE;
+ } else
+ no_url = TRUE;
+
+ if( (a->title != NULL) && (b->title != NULL) ) {
+ atit = conv_unmime_header(a->title, CS_UTF_8, FALSE);
+ btit = conv_unmime_header(b->title, CS_UTF_8, FALSE);
+ if( !strcmp(atit, btit) )
+ title_eq = TRUE;
+ g_free(atit);
+ g_free(btit);
+ } else
+ no_title = TRUE;
+
+ /* If there's no 'published' timestamp for the item, we can only judge
+ * by item url - 'modified' timestamp can have changed if the item was
+ * updated recently. */
+ if( b->date_published <= 0 ) {
+ if( b->date_modified > 0 ) {
+ /* If the item has 'modified' timestamp, we can only rely on url
+ * and title at this point. */
+ if( (url_eq || no_url) && title_eq
+ && (a->date_modified >= b->date_modified) )
+ return 0;
+ else
+ return 1;
+ } else {
+ /* No timestamp of any kind, we'll just assume if both title and url
+ * match, we found the right item. Items in such feeds rarely change,
+ * and if they do, there's no way we can really */
+ if( (url_eq || no_url) && title_eq )
+ return 0;
+ else
+ return 1;
+ }
+ }
+
+ /* Check if 'published' or at least 'modified' timestamps match */
+ if( ((a->date_published > 0) && (b->date_published > 0) &&
+ (a->date_published == b->date_published))
+ || ((a->date_modified > 0) && (b->date_modified > 0) &&
+ (a->date_modified == b->date_modified))) {
+ date_eq = TRUE;
+ } else
+ no_date = TRUE;
+
+ /* If 'published' time and item url match, it is reasonable to assume
+ * we found our item. */
+ if( (no_url || url_eq) && date_eq )
+ return 0;
+
+ /* There is no timestamp and the url matches (or there is none),
+ * we need to compare titles, ... */
+ if( (no_url || url_eq) && no_date ) {
+ if( title_eq )
+ return 0;
+ else
+ return 1;
+
+ /* ... and as a last resort, if there is no title, item texts. */
+ if( no_title && a->text && b->text ) {
+ if( !strcmp(a->text, b->text) )
+ return 0;
+ else
+ return 1;
+ }
+ }
+
+ /* We don't know this item. */
+ return 1;
+}
+
+enum {
+ ITEM_UNCHANGED,
+ ITEM_CHANGED_TEXTONLY,
+ ITEM_CHANGED
+};
+
+static gint rssyl_feed_item_changed(FeedItem *new_item, FeedItem *old_item )
+{
+ debug_print("RSSyl: comparing '%s' and '%s'\n",
+ new_item->title, old_item->title);
+
+ /* if both have title ... */
+ if( old_item->title && new_item->title ) {
+ gchar *old = conv_unmime_header(old_item->title, CS_UTF_8, FALSE);
+ gchar *new = conv_unmime_header(new_item->title, CS_UTF_8, FALSE);
+ if( strcmp(old, new) != 0 ) { /* ... compare "unmimed" titles */
+ debug_print("RSSyl:\t\titem titles differ:\nOLD: '%s'\nNEW: '%s'\n",
+ old, new);
+ g_free(old);
+ g_free(new);
+ return ITEM_CHANGED;
+ }
+ g_free(old);
+ g_free(new);
+ } else {
+ /* if atleast one has a title, they differ */
+ if( old_item->title || new_item->title ) {
+ debug_print("RSSyl:\t\t+/- title\n");
+ return ITEM_CHANGED;
+ }
+ }
+
+ if( old_item->author && new_item->author ) {
+ gchar *old = conv_unmime_header(old_item->author, CS_UTF_8, TRUE);
+ gchar *new = conv_unmime_header(new_item->author, CS_UTF_8, TRUE);
+ if( strcmp(old, new) ) { /* ... compare "unmimed" authors */
+ g_free(old);
+ g_free(new);
+ debug_print("RSSyl:\t\titem authors differ\n");
+ return ITEM_CHANGED;
+ }
+ g_free(old);
+ g_free(new);
+ } else {
+ /* if atleast one has author, they differ */
+ if( old_item->author || new_item->author ) {
+ debug_print("RSSyl:\t\t+/- author\n");
+ return ITEM_CHANGED;
+ }
+ }
+
+ /* if both have text ... */
+ if( old_item->text && new_item->text ) {
+ if( strcmp(old_item->text, new_item->text) ) { /* ... compare them */
+ debug_print("RSSyl:\t\titem texts differ\n");
+ debug_print("\nOLD: '%s'\n", old_item->text);
+ debug_print("\nNEW: '%s'\n", new_item->text);
+
+ return ITEM_CHANGED_TEXTONLY;
+ }
+ } else {
+ /* if at least one has some text, they differ */
+ if( old_item->text || new_item->text ) {
+ debug_print("RSSyl:\t\t+/- text\n");
+ return ITEM_CHANGED_TEXTONLY;
+ }
+ }
+
+ /* they don't seem to differ */
+ return ITEM_UNCHANGED;
+}
+
+enum {
+ EXISTS_NEW,
+ EXISTS_UNCHANGED,
+ EXISTS_CHANGED,
+ EXISTS_CHANGED_TEXTONLY
+};
+
+/* rssyl_feed_item_exists()
+ *
+ * Returns 1 if a feed item already exists locally, 2 if there's a changed
+ * item with link that already belongs to existing item, 3 if only item's
+ * text has changed, 0 if item is new.
+ */
+
+static guint rssyl_feed_item_exists(RFolderItem *ritem, FeedItem *fitem,
+ FeedItem **oldfitem)
+{
+ GSList *item = NULL;
+ FeedItem *efitem = NULL;
+ gint changed;
+
+ g_return_val_if_fail(ritem != NULL, FALSE);
+ g_return_val_if_fail(fitem != NULL, FALSE);
+
+ if( ritem->items == NULL || g_slist_length(ritem->items) == 0 )
+ return EXISTS_NEW;
+
+ if( (item = g_slist_find_custom(ritem->items,
+ (gconstpointer)fitem, (GCompareFunc)rssyl_cb_feed_compare)) ) {
+ efitem = (FeedItem *)item->data;
+ if( (changed = rssyl_feed_item_changed(fitem, efitem)) > ITEM_UNCHANGED ) {
+ *oldfitem = efitem;
+ if (changed == ITEM_CHANGED_TEXTONLY)
+ return EXISTS_CHANGED_TEXTONLY;
+ else
+ return EXISTS_CHANGED;
+ }
+
+ return EXISTS_UNCHANGED;
+ }
+
+ return EXISTS_NEW;
+}
+
+/* =============================================================== */
+
+void rssyl_add_item(RFolderItem *ritem, FeedItem *feed_item)
+{
+ FeedItem *old_item = NULL;
+ MsgFlags *flags;
+ MsgPermFlags oldperm_flags = 0;
+ MsgInfo *msginfo;
+ FILE *f;
+ gint fd, d, dif;
+ time_t tmpd;
+ gchar *meta_charset = NULL;
+ gchar *baseurl = NULL;
+ gchar *template = NULL;
+ gchar *tmp = NULL, *tmpurl = NULL, *tmpid = NULL;
+ gchar *dirname = NULL;
+ gchar *text = NULL;
+ gchar *heading = NULL;
+ gchar hdr[1024];
+ FeedItemEnclosure *enc = NULL;
+ RFeedCtx *ctx;
+
+ g_return_if_fail(ritem != NULL);
+
+ /* If item title is empty, try to fill it from source title (Atom only). */
+ debug_print(",%s,\n", (feed_item->title ? feed_item->title : "<null>"));
+ debug_print(",%s,\n", (feed_item->sourcetitle ? feed_item->sourcetitle : "<null>"));
+ tmp = feed_item_get_sourcetitle(feed_item);
+ if( feed_item_get_title(feed_item) == NULL ||
+ strlen(feed_item->title) == 0 ) {
+ if( tmp != NULL && strlen(tmp) > 0 )
+ feed_item_set_title(feed_item, tmp);
+ else
+ feed_item_set_title(feed_item, C_("Empty RSS feed title placeholder", "(empty)"));
+ }
+
+ debug_print(",%s,\n", (feed_item->title ? feed_item->title : "<null>"));
+ debug_print(",%s,\n", (feed_item->sourcetitle ? feed_item->sourcetitle : "<null>"));
+ if (feed_item_get_id(feed_item) == NULL) {
+ debug_print("RSSyl: item ID empty, using its URL as ID.\n");
+ feed_item_set_id(feed_item, feed_item_get_url(feed_item));
+ }
+
+ /* If neither item date is set, use date from source (Atom only). */
+ if( feed_item_get_date_modified(feed_item) == -1 &&
+ feed_item_get_date_published(feed_item) == -1 )
+ feed_item_set_date_published(feed_item,
+ feed_item_get_sourcedate(feed_item));
+
+ /* Fix up subject, url and ID (rssyl_format_string()) so that
+ * comparing doesn't break. */
+ debug_print("RSSyl: fixing up subject '%s'\n", feed_item_get_title(feed_item));
+ feed_item_set_title(feed_item, rssyl_format_string(feed_item_get_title(feed_item), TRUE, TRUE));
+ debug_print("RSSyl: fixing up URL\n");
+ feed_item_set_url(feed_item, rssyl_format_string(feed_item_get_url(feed_item),
+ TRUE, TRUE));
+ if( feed_item_get_id(feed_item) != NULL ) {
+ debug_print("RSSyl: fixing up ID\n");
+ feed_item_set_id(feed_item, rssyl_format_string(feed_item_get_id(feed_item),
+ TRUE, TRUE));
+ }
+
+ /* If there's a summary, but no text, use summary as text. */
+ if( feed_item_get_text(feed_item) == NULL &&
+ (tmp = feed_item_get_summary(feed_item)) != NULL ) {
+ feed_item_set_text(feed_item, tmp);
+ g_free(feed_item->summary); /* We do not need summary in rssyl now. */
+ feed_item->summary = NULL;
+ }
+
+ /* Do not add if the item already exists, update if it does exist, but
+ * has changed. */
+ dif = rssyl_feed_item_exists(ritem, feed_item, &old_item);
+ debug_print("RSSyl: rssyl_feed_item_exists returned %d\n", dif);
+
+ if( dif == EXISTS_UNCHANGED ) {
+ debug_print("RSSyl: This item already exists, skipping...\n");
+ return;
+ }
+
+ /* Item is already in the list, but has changed */
+ if( dif >= EXISTS_CHANGED && old_item != NULL ) {
+ debug_print("RSSyl: Item changed, removing old one and adding new.\n");
+
+ /* Store permflags of the old item. */
+ ctx = (RFeedCtx *)old_item->data;
+ msginfo = folder_item_get_msginfo((FolderItem *)ritem,
+ atoi(g_path_get_basename(ctx->path)));
+ oldperm_flags = msginfo->flags.perm_flags;
+
+ ritem->items = g_slist_remove(ritem->items, old_item);
+ g_remove(ctx->path);
+
+ g_free(ctx->path);
+ feed_item_free(old_item);
+ old_item = NULL;
+ }
+
+ /* Check against list of deleted items. */
+ if (rssyl_deleted_check(ritem->deleted_items, feed_item)) {
+ debug_print("RSSyl: Item '%s' found among deleted items, NOT adding it.\n",
+ feed_item_get_title(feed_item));
+ return;
+ }
+
+ /* Add a new item, formatting its title along the way */
+ debug_print("RSSyl: Adding item '%s'\n", feed_item_get_title(feed_item));
+ ritem->items = g_slist_prepend(ritem->items, feed_item_copy(feed_item));
+
+ dirname = folder_item_get_path(&ritem->item);
+ template = g_strconcat(dirname, G_DIR_SEPARATOR_S,
+ RSSYL_TMP_TEMPLATE, NULL);
+ fd = mkstemp(template);
+
+ f = fdopen(fd, "w");
+ if(f == NULL) {
+ g_warning("Couldn't open file '%s', not adding msg!\n", template);
+ g_free(template);
+ return;
+ }
+
+ /* From */
+ if( (tmp = feed_item_get_author(feed_item)) != NULL ) {
+ if( g_utf8_validate(tmp, -1, NULL)) {
+ conv_encode_header_full(hdr, 1023, tmp, strlen("From: "),
+ TRUE, CS_UTF_8);
+ fprintf(f, "From: %s\n", hdr);
+ } else
+ fprintf(f, "From: %s\n", tmp);
+ }
+
+ /* Date */
+ if( (tmpd = feed_item_get_date_modified(feed_item)) != -1 ) {
+ tmp = createRFC822Date(&tmpd);
+ debug_print("RSSyl: using date_modified: '%s'\n", tmp);
+ } else if( (tmpd = feed_item_get_date_published(feed_item)) != -1 ) {
+ tmp = createRFC822Date(&tmpd);
+ debug_print("RSSyl: using date_published: '%s'\n", tmp);
+ } else {
+ tmpd = time(NULL);
+ tmp = createRFC822Date(&tmpd);
+ }
+
+ if( tmp != NULL ) {
+ fprintf(f, "Date: %s\n", tmp);
+ g_free(tmp);
+ }
+
+ if( (tmp = feed_item_get_title(feed_item)) != NULL ) {
+
+ /* (Atom only) Strip HTML markup from title for the Subject line. */
+ if( feed_item_get_title_format(feed_item) == FEED_ITEM_TITLE_HTML
+ || feed_item_get_title_format(feed_item) == FEED_ITEM_TITLE_XHTML) {
+ debug_print("RSSyl: item title is HTML/XHTML, stripping tags for Subject line\n");
+ tmp = g_strdup(tmp);
+ strip_html(tmp);
+ }
+
+ if( g_utf8_validate(tmp, -1, NULL) ) {
+ conv_encode_header_full(hdr, 1023, tmp, strlen("Subject: "),
+ FALSE, CS_UTF_8);
+ debug_print("RSSyl: Subject: %s\n", hdr);
+ fprintf(f, "Subject: %s\n", hdr);
+ } else
+ fprintf(f, "Subject: %s\n", tmp);
+
+ if( feed_item_get_title_format(feed_item) == FEED_ITEM_TITLE_HTML
+ || feed_item_get_title_format(feed_item) == FEED_ITEM_TITLE_XHTML) {
+ g_free(tmp);
+ fprintf(f, "X-RSSyl-OrigTitle: %s\n", feed_item_get_title(feed_item));
+ }
+ } else {
+ debug_print("RSSyl: No feed title, it seems\n");
+ fprintf(f, "Subject: (empty)\n");
+ }
+
+ /* X-RSSyl-URL */
+ if( (tmpurl = feed_item_get_url(feed_item)) == NULL ) {
+ if( feed_item_get_id(feed_item) != NULL &&
+ feed_item_id_is_permalink(feed_item) ) {
+ tmpurl = feed_item_get_id(feed_item);
+ }
+ }
+
+ if( tmpurl != NULL )
+ fprintf(f, "X-RSSyl-URL: %s\n", tmpurl);
+
+ if( ritem->last_update > 0) {
+ fprintf(f, "X-RSSyl-Last-Seen: %ld\n", ritem->last_update);
+ }
+
+ /* Message-ID */
+ if( (tmpid = feed_item_get_id(feed_item)) == NULL )
+ tmpid = feed_item_get_url(feed_item);
+ if( tmpid != NULL )
+ fprintf(f, "Message-ID: <%s>\n", tmpid);
+
+ /* X-RSSyl-Comments */
+ if( (text = feed_item_get_comments_url(feed_item)) != NULL )
+ fprintf(f, "X-RSSyl-Comments: %s\n", text);
+
+ /* References */
+ if( (text = feed_item_get_parent_id(feed_item)) != NULL )
+ fprintf(f, "References: <%s>\n", text);
+
+ /* Content-Type */
+ text = feed_item_get_text(feed_item);
+ if( text && g_utf8_validate(text, -1, NULL) ) {
+ fprintf(f, "Content-Type: text/html; charset=UTF-8\n\n");
+ meta_charset = g_strdup("<meta http-equiv=\"Content-Type\" "
+ "content=\"text/html; charset=UTF-8\">");
+ } else {
+ fprintf(f, "Content-Type: text/html\n\n");
+ }
+
+ /* construct base href */
+ if( feed_item_get_url(feed_item) != NULL )
+ baseurl = g_strdup_printf("<base href=\"%s\">\n",
+ feed_item_get_url(feed_item) );
+
+ if( ritem->write_heading )
+ heading = g_strdup_printf("<h2>%s</h2>\n<br><br>\n",
+ feed_item_get_title(feed_item));
+
+ /* Message body */
+ fprintf(f, "<html><head>"
+ "%s\n"
+ "%s"
+ "</head>\n<body>\n"
+ "%s\n"
+ "URL: <a href=\"%s\">%s</a>\n\n<br><br>\n"
+ RSSYL_TEXT_START"\n"
+ "%s%s"
+ RSSYL_TEXT_END"\n\n",
+ (meta_charset ? meta_charset : ""),
+ (baseurl ? baseurl : ""),
+ (heading ? heading : ""),
+ (tmpurl ? tmpurl : ""),
+ (tmpurl ? tmpurl : "n/a"),
+ (text ? text : ""), (text ? "\n" : "") );
+
+ g_free(meta_charset);
+ g_free(baseurl);
+ g_free(heading);
+
+ if( (enc = feed_item_get_enclosure(feed_item)) != NULL )
+ fprintf(f, "<p><a href=\"%s\">Attached media file</a> [%s] (%ld bytes)</p>\n",
+ feed_item_enclosure_get_url(enc),
+ feed_item_enclosure_get_type(enc),
+ feed_item_enclosure_get_size(enc) );
+
+ fprintf(f, "</body></html>\n");
+ fclose(f);
+
+ g_return_if_fail(template != NULL);
+
+ flags = g_new(MsgFlags, 1);
+ flags->perm_flags = MSG_NEW | MSG_UNREAD;
+ flags->tmp_flags = 0;
+
+ d = folder_item_add_msg(&ritem->item, template, flags, TRUE);
+ g_free(template);
+
+ ctx = g_new0(RFeedCtx, 1);
+ ctx->path = (gpointer)g_strdup_printf("%s%c%d", dirname,
+ G_DIR_SEPARATOR, d);
+ ctx->last_seen = ritem->last_update;
+ ((FeedItem *)ritem->items->data)->data = (gpointer)ctx;
+
+ /* Unset unread+new if the changed item wasn't set unread and user
+ * doesn't want to see it unread because of the change. */
+ if (!(oldperm_flags & MSG_UNREAD) && (ritem->silent_update == 2
+ || (ritem->silent_update == 1 && dif == EXISTS_CHANGED_TEXTONLY)))
+ procmsg_msginfo_unset_flags(
+ folder_item_get_msginfo((FolderItem *)ritem, d), MSG_NEW | MSG_UNREAD, 0);
+
+ debug_print("RSSyl: folder_item_add_msg(): %d\n", d);
+}
diff --git a/src/plugins/rssyl/rssyl_add_item.h b/src/plugins/rssyl/rssyl_add_item.h
new file mode 100644
index 0000000..5e5e4e3
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_add_item.h
@@ -0,0 +1,6 @@
+#ifndef __RSSYL_ADD_ITEM_H
+#define __RSSYL_ADD_ITEM_H
+
+void rssyl_add_item(RFolderItem *ritem, FeedItem *feed_item);
+
+#endif /* __RSSYL_ADD_ITEM_H */
diff --git a/src/plugins/rssyl/rssyl_cb_gtk.c b/src/plugins/rssyl/rssyl_cb_gtk.c
deleted file mode 100644
index 0e49bb9..0000000
--- a/src/plugins/rssyl/rssyl_cb_gtk.c
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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;
-}
diff --git a/src/plugins/rssyl/rssyl_cb_gtk.h b/src/plugins/rssyl/rssyl_cb_gtk.h
deleted file mode 100644
index a49d3a3..0000000
--- a/src/plugins/rssyl/rssyl_cb_gtk.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#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 */
diff --git a/src/plugins/rssyl/rssyl_cb_menu.c b/src/plugins/rssyl/rssyl_cb_menu.c
index f3047de..d0c2ef9 100644
--- a/src/plugins/rssyl/rssyl_cb_menu.c
+++ b/src/plugins/rssyl/rssyl_cb_menu.c
@@ -1,5 +1,5 @@
/*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Claws-Mail-- 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>
*
@@ -22,34 +22,38 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
-#include "claws-features.h"
#endif
+/* Global includes */
#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"
+/* Claws Mail includes */
+#include <folderview.h>
+#include <alertpanel.h>
+#include <gtk/inputdialog.h>
+#include <prefs_common.h>
+#include <folder_item_prefs.h>
+#include <filesel.h>
+#include <inc.h>
+
+/* Local includes */
+#include "libfeed/parser_opml.h"
#include "rssyl_gtk.h"
-
-void rssyl_new_feed_cb(GtkAction *action, gpointer data)
+#include "rssyl_feed.h"
+#include "rssyl_feed_props.h"
+#include "rssyl_update_feed.h"
+#include "rssyl_subscribe.h"
+#include "opml_import.h"
+
+void rssyl_new_feed_cb(GtkAction *action,
+ gpointer data)
{
- FolderView *folderview = (FolderView *)data;
+ FolderView *folderview = (FolderView*)data;
GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
FolderItem *item;
- gchar *new_feed;
+ gchar *url;
debug_print("RSSyl: new_feed_cb\n");
@@ -59,26 +63,26 @@ void rssyl_new_feed_cb(GtkAction *action, gpointer data)
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);
+ url = input_dialog(_("Subscribe feed"),
+ _("Input the URL of the news feed you wish to subscribe:"),
+ "");
+ if( url == NULL ) /* User cancelled */
+ return;
- rssyl_subscribe_new_feed(item, new_feed, TRUE);
+ rssyl_subscribe(item, url, TRUE);
- g_free(new_feed);
+ g_free(url);
}
-void rssyl_new_folder_cb(GtkAction *action, gpointer data)
+void rssyl_new_folder_cb(GtkAction *action,
+ gpointer data)
{
- FolderView *folderview = (FolderView *)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;
+ gchar *new_folder, *p, *tmp;
+ gint i = 1;
if (!folderview->selected) return;
@@ -90,132 +94,44 @@ void rssyl_new_folder_cb(GtkAction *action, gpointer data)
_("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."),
+ alertpanel_error(_("'%c' can't be used in folder name."),
G_DIR_SEPARATOR);
+ g_free(new_folder);
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;
+ /* Find an unused name for new folder */
+ /* TODO: Perhaps stop after X attempts? */
+ tmp = g_strdup(new_folder);
+ while (folder_find_child_item_by_name(item, tmp)) {
+ debug_print("RSSyl: Folder '%s' already exists, trying another name\n",
+ new_folder);
+ g_free(tmp);
+ tmp = g_strdup_printf("%s__%d", new_folder, ++i);
}
+ g_free(new_folder);
+ new_folder = tmp;
+
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);
+ alertpanel_error(_("Can't create the folder '%s'."), new_folder);
+ g_free(new_folder);
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;
- }
+ g_free(new_folder);
folder_write_list();
}
-void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
+void rssyl_remove_folder_cb(GtkAction *action,
+ gpointer data)
{
- FolderView *folderview = (FolderView *)data;
+ FolderView *folderview = (FolderView*)data;
GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
FolderItem *item;
gchar *message, *name;
@@ -263,45 +179,82 @@ void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
}
-void rssyl_refresh_cb(GtkAction *action, gpointer data)
+void rssyl_rename_cb(GtkAction *action,
+ gpointer *data)
{
- FolderView *folderview = (FolderView *)data;
FolderItem *item;
- RSSylFolderItem *ritem;
-
+ gchar *new_folder;
+ gchar *name;
+ gchar *message;
+ FolderView *folderview = (FolderView*)data;
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);
- ritem = (RSSylFolderItem *)item;
+ name = trim_string(item->name, 32);
+ message = g_strdup_printf(_("Input new name for '%s':"), name);
+ new_folder = input_dialog(_("Rename folder"), message, 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 (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;
+ 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;
}
- main_window_cursor_wait(mainwindow_get_mainwindow());
- rssyl_update_feed(ritem);
- main_window_cursor_normal(mainwindow_get_mainwindow());
+ 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(item);
+ folder_write_list();
}
-void rssyl_refresh_all_cb(GtkAction *action, gpointer data)
+void rssyl_refresh_feed_cb(GtkAction *action,
+ gpointer data)
{
- main_window_cursor_wait(mainwindow_get_mainwindow());
- rssyl_refresh_all_feeds();
- main_window_cursor_normal(mainwindow_get_mainwindow());
+ FolderView *folderview = (FolderView*)data;
+ FolderItem *item = NULL;
+ RFolderItem *ritem = NULL;
+
+ item = folderview_get_selected_item(folderview);
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ ritem = (RFolderItem *)item;
+
+ /* Offline check */
+ if( prefs_common.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 feeds.", 1))) {
+ return;
+ }
+
+ /* Update feed, displaying errors if any. */
+ rssyl_update_feed(ritem, TRUE);
}
void rssyl_prop_cb(GtkAction *action, gpointer data)
{
- FolderView *folderview = (FolderView *)data;
+ FolderView *folderview = (FolderView*)data;
FolderItem *item;
- RSSylFolderItem *ritem;
+ RFolderItem *ritem;
item = folderview_get_selected_item(folderview);
g_return_if_fail(item != NULL);
@@ -309,74 +262,103 @@ void rssyl_prop_cb(GtkAction *action, gpointer data)
debug_print("RSSyl: rssyl_prop_cb() for '%s'\n", item->name);
- ritem = (RSSylFolderItem *)item;
- rssyl_get_feed_props(ritem);
+ ritem = (RFolderItem *)item;
+
rssyl_gtk_prop(ritem);
}
-void rssyl_rename_cb(GtkAction *action, gpointer data)
+void rssyl_update_all_cb( GtkAction *action, gpointer data)
{
- FolderView *folderview = (FolderView *)data;
FolderItem *item;
- gchar *new_folder;
- gchar *name;
- gchar *message;
+ FolderView *folderview = (FolderView*)data;
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;});
+ debug_print("RSSyl: rssyl_update_all_cb(): clicked on '%s'\n", item->name);
- if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
- alertpanel_error(_("'%c' can't be included in folder name."),
- G_DIR_SEPARATOR);
+ if( item->folder->klass != rssyl_folder_get_class() ) {
+ debug_print("RSSyl: this is not a RSSyl folder, returning\n");
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);
+ rssyl_update_recursively(item);
+}
+
+void rssyl_remove_mailbox_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *item = NULL;
+ gchar *n, *message;
+ AlertValue avalue;
+
+ item = folderview_get_selected_item(folderview);
+
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ if( folder_item_parent(item) )
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."));
+ n = trim_string(item->folder->name, 32);
+ message = g_strdup_printf(_("Really remove the feed tree `%s' ?\n"), n);
+ avalue = alertpanel_full(_("Remove feed tree"), message,
+ GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE,
+ NULL, ALERT_WARNING, G_ALERTDEFAULT);
+ g_free(message);
+ g_free(n);
+
+ if( avalue != G_ALERTALTERNATE )
+ return;
+
+ folderview_unselect(folderview);
+ summary_clear_all(folderview->summaryview);
+
+ n = folder_item_get_path(item);
+ if( remove_dir_recursive(n) < 0 ) {
+ g_warning("can't remove directory '%s'\n", n);
+ g_free(n);
return;
}
- folder_item_prefs_save_config_recursive(item);
- folder_write_list();
+ g_free(n);
+ folder_destroy(item->folder);
}
void rssyl_import_feed_list_cb(GtkAction *action, gpointer data)
{
FolderView *folderview = (FolderView *)data;
- debug_print("RSSyl: rssyl_import_feed_cb\n");
+ GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+ FolderItem *item = NULL;
+ gchar *path = NULL;
+ OPMLImportCtx *ctx = NULL;
- FolderItem *item;
- gchar *opmlfile = NULL;
+ debug_print("RSSyl: import_feed_list_cb\n");
- item = folderview_get_selected_item(folderview);
+ /* Ask user for a file to import */
+ path = filesel_select_file_open_with_filter(
+ _("Select an OPML file"), NULL, "*.opml");
+ if (!is_file_exist(path)) {
+ g_free(path);
+ return;
+ }
+
+ /* Find the destination folder for the import */
+ 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);
- opmlfile = filesel_select_file_open_with_filter(
- _("Select a .opml file"), NULL, "*.opml");
-
- if (!is_file_exist(opmlfile)) {
- g_free(opmlfile);
- return;
- }
+ ctx = malloc( sizeof(OPMLImportCtx) );
+ ctx->failures = 0;
+ ctx->depth = rssyl_folder_depth(item);
+ ctx->current = NULL;
+ ctx->current = g_slist_append(ctx->current, item);
+
+ /* Start the whole shebang - call libfeed's OPML parser with correct
+ * user function */
+ opml_process(path, rssyl_opml_import_func, (gpointer)ctx);
- rssyl_opml_import(opmlfile, item);
+ g_free(ctx);
}
diff --git a/src/plugins/rssyl/rssyl_cb_menu.h b/src/plugins/rssyl/rssyl_cb_menu.h
index 098f43c..258f9a5 100644
--- a/src/plugins/rssyl/rssyl_cb_menu.h
+++ b/src/plugins/rssyl/rssyl_cb_menu.h
@@ -7,14 +7,13 @@
#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);
+void rssyl_refresh_feed_cb(GtkAction *action, gpointer data);
+void rssyl_prop_cb(GtkAction *action, gpointer data);
+void rssyl_update_all_cb(GtkAction *action, gpointer data);
+void rssyl_remove_mailbox_cb(GtkAction *action, gpointer data);
+void rssyl_import_feed_list_cb(GtkAction *action, gpointer data);
#endif /* __RSSYL_MENU_FEED */
diff --git a/src/plugins/rssyl/rssyl_deleted.c b/src/plugins/rssyl/rssyl_deleted.c
new file mode 100644
index 0000000..2d39adb
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_deleted.c
@@ -0,0 +1,354 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * - handling of info about user-deleted feed 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"
+#endif
+
+/* Global includes */
+#include <glib.h>
+#include <string.h>
+
+/* Claws Mail includes */
+#include <codeconv.h>
+#include <common/utils.h>
+
+/* Local includes */
+#include "rssyl.h"
+#include "parse822.h"
+#include "strutils.h"
+
+static RDeletedItem *_new_deleted_item()
+{
+ RDeletedItem *ditem = g_new0(RDeletedItem, 1);
+
+ ditem->id = NULL;
+ ditem->title = NULL;
+ ditem->date_published = -1;
+ ditem->date_modified = -1;
+
+ return ditem;
+}
+
+static void _free_deleted_item(gpointer d, gpointer user_data)
+{
+ RDeletedItem *ditem = (RDeletedItem *)d;
+
+ if (ditem == NULL)
+ return;
+
+ g_free(ditem->id);
+ g_free(ditem->title);
+ g_free(ditem);
+}
+
+void rssyl_deleted_free(GSList *deleted_items)
+{
+ if (deleted_items != NULL) {
+ debug_print("RSSyl: releasing list of deleted items\n");
+ g_slist_foreach(deleted_items, _free_deleted_item, NULL);
+ g_slist_free(deleted_items);
+ deleted_items = NULL;
+ }
+}
+
+static gchar * _deleted_file_path(RFolderItem *ritem)
+{
+ gchar *itempath, *deleted_file;
+
+ itempath = folder_item_get_path(&ritem->item);
+ deleted_file = g_strconcat(itempath, G_DIR_SEPARATOR_S, RSSYL_DELETED_FILE, NULL);
+ g_free(itempath);
+
+ return deleted_file;
+}
+
+/***************************************************************/
+GSList *rssyl_deleted_update(RFolderItem *ritem)
+{
+ gchar *deleted_file, *contents, **lines, **line;
+ GError *error = NULL;
+ guint i = 0;
+ RDeletedItem *ditem = NULL;
+ GSList *deleted_items = NULL;
+
+ g_return_val_if_fail(ritem != NULL, NULL);
+
+ deleted_file = _deleted_file_path(ritem);
+
+ debug_print("RSSyl: getting list of deleted items from '%s'\n", deleted_file);
+
+ if (!g_file_test(deleted_file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ debug_print("RSSyl: '%s' doesn't exist, ignoring\n", deleted_file);
+ return NULL;
+ }
+
+ g_file_get_contents(deleted_file, &contents, NULL, &error);
+
+ if (error)
+ g_warning("GError: '%s'\n", error->message);
+
+ if (contents != NULL) {
+ lines = strsplit_no_copy(contents, '\n');
+ } else {
+ g_warning("Couldn't read '%s', ignoring\n", deleted_file);
+ g_free(deleted_file);
+ return NULL;
+ }
+
+ g_free(deleted_file);
+
+ while (lines[i]) {
+ line = g_strsplit(lines[i], ": ", 2);
+ if (line[0] && line[1] && strlen(line[0]) && strlen(line[1])) {
+ if (!strcmp(line[0], "ID")) {
+ ditem = _new_deleted_item();
+ ditem->id = g_strdup(line[1]);
+ } else if (ditem != NULL && !strcmp(line[0], "TITLE")) {
+ ditem->title = g_strdup(line[1]);
+ } else if (ditem != NULL && !strcmp(line[0], "DPUB")) {
+ ditem->date_published = atoi(line[1]);
+ } else if (ditem != NULL && !strcmp(line[0], "DMOD")) {
+ ditem->date_modified = atoi(line[1]);
+ deleted_items = g_slist_prepend(deleted_items, ditem);
+ ditem = NULL;
+ }
+ }
+
+ g_strfreev(line);
+ i++;
+ }
+
+ g_free(lines);
+ g_free(contents);
+
+ debug_print("RSSyl: got %d deleted items\n", g_slist_length(deleted_items));
+ return deleted_items;
+}
+
+static void _store_one_deleted_item(gpointer data, gpointer user_data)
+{
+ RDeletedItem *ditem = (RDeletedItem *)data;
+ FILE *f = (FILE *)user_data;
+ gboolean err = FALSE;
+
+ if (ditem == NULL || ditem->id == NULL)
+ return;
+
+ err |= (fprintf(f,
+ "ID: %s\n"
+ "TITLE: %s\n"
+ "DPUB: %ld\n"
+ "DMOD: %ld\n",
+ ditem->id, ditem->title,
+ ditem->date_published, ditem->date_modified) < 0);
+
+ if (err)
+ debug_print("RSSyl: Error during writing deletion file.\n");
+}
+
+static void rssyl_deleted_store_internal(GSList *deleted_items, const gchar *deleted_file)
+{
+ FILE *f;
+
+ if (g_file_test(deleted_file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+ g_remove(deleted_file);
+
+ if (g_slist_length(deleted_items) == 0)
+ return;
+
+ if ((f = g_fopen(deleted_file, "w")) == NULL) {
+ debug_print("RSSyl: Couldn't open '%s', ignoring.\n", deleted_file);
+ return;
+ }
+
+ g_slist_foreach(deleted_items, (GFunc)_store_one_deleted_item,
+ (gpointer)f);
+
+ fclose(f);
+ debug_print("RSSyl: written and closed deletion file\n");
+}
+
+void rssyl_deleted_store(RFolderItem *ritem)
+{
+ gchar *path;
+
+ g_return_if_fail(ritem != NULL);
+
+ path = _deleted_file_path(ritem);
+ rssyl_deleted_store_internal(ritem->deleted_items, path);
+}
+
+
+/* Creates a FeedItem from a message file and uses the data to add a item
+ * to the list of deleted stuff. */
+void rssyl_deleted_add(RFolderItem *ritem, gchar *path)
+{
+ FeedItem *fitem = NULL;
+ RDeletedItem *ditem = NULL;
+ GSList *deleted_items = rssyl_deleted_update(ritem);
+ gchar *deleted_file = NULL;
+
+ if (!(fitem = rssyl_parse_folder_item_file(path)))
+ return;
+
+ ditem = _new_deleted_item();
+ ditem->id = g_strdup(feed_item_get_id(fitem));
+ ditem->title = conv_unmime_header(feed_item_get_title(fitem),
+ CS_UTF_8, FALSE);
+ ditem->date_published = feed_item_get_date_published(fitem);
+ ditem->date_modified = feed_item_get_date_modified(fitem);
+
+ deleted_items = g_slist_prepend(deleted_items, ditem);
+
+ deleted_file = _deleted_file_path(ritem);
+ rssyl_deleted_store_internal(deleted_items, deleted_file);
+ g_free(deleted_file);
+
+ rssyl_deleted_free(deleted_items);
+}
+
+static gint _rssyl_deleted_check_func(gconstpointer a, gconstpointer b)
+{
+ RDeletedItem *ditem = (RDeletedItem *)a;
+ FeedItem *fitem = (FeedItem *)b;
+
+ g_return_val_if_fail(ditem != NULL, -10);
+ g_return_val_if_fail(fitem != NULL, -20);
+
+ /* Following must match:
+ * ID, ... */
+ if (!ditem->id || !feed_item_get_id(fitem) ||
+ strcmp(ditem->id, feed_item_get_id(fitem)))
+ return -1;
+
+ /* title, ... */
+ if (!ditem->title || !feed_item_get_title(fitem) ||
+ strcmp(ditem->title, feed_item_get_title(fitem)))
+ return -2;
+
+ /* time of publishing, ... */
+ if (ditem->date_published != -1 &&
+ ditem->date_published != feed_item_get_date_published(fitem))
+ return -3;
+
+ /* and the time of last modification must be greater
+ * (this means that the item was updated since deletion, and possibly
+ * contains new data, so we add it again) */
+ if (ditem->date_modified != -1 &&
+ ditem->date_modified < feed_item_get_date_modified(fitem))
+ return -4;
+
+ return 0;
+}
+
+/* Returns TRUE if fitem is found among the deleted stuff. */
+gboolean rssyl_deleted_check(GSList *deleted_items, FeedItem *fitem)
+{
+ if (g_slist_find_custom(deleted_items, (gconstpointer)fitem,
+ _rssyl_deleted_check_func) != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/******** Expiring ********/
+struct _RDelExpireCtx {
+ RDeletedItem *ditem;
+ gboolean delete;
+};
+
+typedef struct _RDelExpireCtx RDelExpireCtx;
+
+static void _rssyl_deleted_expire_func_f(gpointer data, gpointer user_data)
+{
+ FeedItem *fitem = (FeedItem *)data;
+ RDelExpireCtx *ctx = (RDelExpireCtx *)user_data;
+
+ /* Following must match:
+ * ID, ... */
+ if (!ctx->ditem->id || !feed_item_get_id(fitem) ||
+ strcmp(ctx->ditem->id, feed_item_get_id(fitem)))
+ return;
+
+ /* title, ... */
+ if (!ctx->ditem->title || !feed_item_get_title(fitem) ||
+ strcmp(ctx->ditem->title, feed_item_get_title(fitem)))
+ return;
+
+ /* time of publishing, ... */
+ if (ctx->ditem->date_published != feed_item_get_date_published(fitem))
+ return;
+
+ /* and the time of last modification must be greater
+ * (this means that the item was updated since deletion, and possibly
+ * contains new data, so we add it again) */
+ if (ctx->ditem->date_modified != feed_item_get_date_modified(fitem))
+ return;
+
+ ctx->delete = FALSE;
+}
+
+/* Checks each item in deleted items list against feed and removes it if
+ * it is not found there anymore. */
+void rssyl_deleted_expire(RFolderItem *ritem, Feed *feed)
+{
+ GSList *d = NULL, *d2;
+ RDelExpireCtx *ctx = NULL;
+ RDeletedItem *ditem;
+
+ g_return_if_fail(ritem != NULL);
+ g_return_if_fail(feed != NULL);
+
+ ritem->deleted_items = rssyl_deleted_update(ritem);
+
+ /* Iterate over all items in the list */
+ d = ritem->deleted_items;
+ while (d) {
+ ditem = (RDeletedItem *)d->data;
+ ctx = g_new0(RDelExpireCtx, 1);
+ ctx->ditem = ditem;
+ ctx->delete = TRUE;
+
+ /* Adjust ctx->delete accordingly */
+ feed_foreach_item(feed, _rssyl_deleted_expire_func_f, (gpointer)ctx);
+
+ /* Remove the item if necessary */
+ if (ctx->delete) {
+ debug_print("RSSyl: (DELETED) removing '%s' from list\n", ditem->title);
+ d2 = d->next;
+ ritem->deleted_items = g_slist_remove_link(ritem->deleted_items, d);
+ d = d2;
+ continue;
+ } else {
+ d = d->next;
+ }
+
+ g_free(ctx);
+ }
+
+ /* Write the new list to disk */
+ rssyl_deleted_store(ritem);
+
+ /* And clean up after myself */
+ rssyl_deleted_free(ritem->deleted_items);
+}
diff --git a/src/plugins/rssyl/rssyl_deleted.h b/src/plugins/rssyl/rssyl_deleted.h
new file mode 100644
index 0000000..d3c198d
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_deleted.h
@@ -0,0 +1,14 @@
+#ifndef __RSSYL_DELETED_H
+#define __RSSYL_DELETED_H
+
+#include <glib.h>
+
+#include "rssyl.h"
+
+GSList *rssyl_deleted_update(RFolderItem *ritem);
+void *rssyl_deleted_free(GSList *deleted_items);
+void rssyl_deleted_add(RFolderItem *ritem, gchar *path);
+gboolean rssyl_deleted_check(GSList *deleted_items, FeedItem *fitem);
+void rssyl_deleted_expire(RFolderItem *ritem, Feed *feed);
+
+#endif /* __RSSYL_DELETED_H */
diff --git a/src/plugins/rssyl/rssyl_feed.c b/src/plugins/rssyl/rssyl_feed.c
new file mode 100644
index 0000000..46edc01
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_feed.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* Claws Mail includes */
+#include <folder.h>
+#include <alertpanel.h>
+#include <procheader.h>
+#include <prefs_common.h>
+#include <mainwindow.h>
+
+/* Local includes */
+#include "libfeed/feeditem.h"
+#include "libfeed/date.h"
+#include "rssyl.h"
+#include "rssyl_feed.h"
+#include "rssyl_prefs.h"
+#include "rssyl_update_feed.h"
+#include "strutils.h"
+
+FolderItem *rssyl_feed_subscribe_new(FolderItem *parent, const gchar *url,
+ gboolean verbose)
+{
+ gchar *myurl = NULL, *tmpname = NULL;
+ FolderItem *new_item = NULL;
+ RFolderItem *ritem = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail(parent != NULL, FALSE);
+ g_return_val_if_fail(url != NULL, FALSE);
+
+ log_print(LOG_PROTOCOL, RSSYL_LOG_SUBSCRIBING, url);
+
+ if( !strncmp(url, "feed://", 7) )
+ myurl = g_strdup(url+7);
+ else if( !strncmp(url, "feed:", 5) )
+ myurl = g_strdup(url+5);
+ else
+ myurl = g_strdup(url);
+
+ myurl = g_strchomp(myurl);
+
+ gtk_cmclist_freeze(GTK_CMCLIST(mainwindow_get_mainwindow()->folderview->ctree));
+ folder_item_update_freeze();
+
+ /* Create a feed folder with generic name. */
+ tmpname = g_strdup_printf("%s.%ld", RSSYL_NEW_FOLDER_NAME, (long int)time(NULL));
+ new_item = folder_create_folder(parent, tmpname);
+ g_free(tmpname);
+ if( !new_item ) {
+ if( verbose )
+ alertpanel_error(_("Couldn't create folder for new feed '%s'."),
+ myurl);
+ g_free(myurl);
+ return NULL;
+ }
+
+ /* Set it up as a RSSyl folder */
+ ritem = (RFolderItem *)new_item;
+ ritem->url = g_strdup(myurl);
+
+ /* Try to update it, delete if failed.
+ * (it is renamed in rssyl_update_feed(). */
+ if( (success = rssyl_update_feed(ritem, verbose)) == FALSE )
+ new_item->folder->klass->remove_folder(new_item->folder, new_item);
+ else {
+ folder_item_scan(new_item);
+ folder_write_list();
+ }
+
+ folder_item_update_thaw();
+ gtk_cmclist_thaw(GTK_CMCLIST(mainwindow_get_mainwindow()->folderview->ctree));
+
+ if( success )
+ log_print(LOG_PROTOCOL, RSSYL_LOG_SUBSCRIBED, ritem->official_title,
+ ritem->url);
+ else {
+ debug_print("RSSyl: Failed to add feed '%s'\n", myurl);
+ g_free(myurl);
+ return NULL;
+ }
+
+ return new_item;
+}
+
+MsgInfo *rssyl_feed_parse_item_to_msginfo(gchar *file, MsgFlags flags,
+ gboolean a, gboolean b, FolderItem *item)
+{
+ MsgInfo *msginfo;
+
+ g_return_val_if_fail(item != NULL, NULL);
+
+ msginfo = procheader_parse_file(file, flags, a, b);
+ if (msginfo)
+ msginfo->folder = item;
+
+ return msginfo;
+}
+
+gboolean rssyl_refresh_timeout_cb(gpointer data)
+{
+ RRefreshCtx *ctx = (RRefreshCtx *)data;
+ time_t tt = time(NULL);
+ gchar *tmpdate = NULL;
+
+ g_return_val_if_fail(ctx != NULL, FALSE);
+
+ if( prefs_common.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("RSSyl: %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, FALSE);
+
+ return TRUE;
+}
+
+void rssyl_feed_start_refresh_timeout(RFolderItem *ritem)
+{
+ RRefreshCtx *ctx;
+ guint source_id;
+ RPrefs *rsprefs = NULL;
+
+ g_return_if_fail(ritem != NULL);
+
+ if( ritem->default_refresh_interval ) {
+ rsprefs = rssyl_prefs_get();
+ if( !rsprefs->refresh_enabled )
+ return;
+ ritem->refresh_interval = rsprefs->refresh;
+ }
+
+ ctx = g_new0(RRefreshCtx, 1);
+ ctx->ritem = ritem;
+
+ source_id = g_timeout_add(ritem->refresh_interval * 60 * 1000,
+ (GSourceFunc)rssyl_refresh_timeout_cb, ctx );
+ ritem->refresh_id = source_id;
+ ctx->id = source_id;
+
+ debug_print("RSSyl: start_refresh_timeout - %d min (id %d)\n",
+ ritem->refresh_interval, ctx->id);
+}
diff --git a/src/plugins/rssyl/rssyl_feed.h b/src/plugins/rssyl/rssyl_feed.h
new file mode 100644
index 0000000..e34fc10
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_feed.h
@@ -0,0 +1,27 @@
+#ifndef __RSSYL_FEED_H
+#define __RSSYL_FEED_H
+
+#include <glib.h>
+
+#include <folder.h>
+
+#include "rssyl.h"
+
+#define RSSYL_LOG_SUBSCRIBING _("RSSyl: Subscribing new feed: %s\n")
+#define RSSYL_LOG_SUBSCRIBED _("RSSyl: New feed subscribed: '%s' (%s)\n")
+#define RSSYL_LOG_UPDATING _("RSSyl: Updating feed: %s\n")
+#define RSSYL_LOG_UPDATED _("RSSyl: Feed update finished: %s\n")
+#define RSSYL_LOG_ERROR_FETCH _("RSSyl: Error fetching feed at '%s': %s\n")
+#define RSSYL_LOG_ERROR_NOFEED _("RSSyl: No valid feed found at '%s'\n")
+#define RSSYL_LOG_ERROR_PROC _("RSSyl: Couldn't process feed at '%s'\n")
+#define RSSYL_LOG_ABORTED_EXITING _("RSSyl: Application is exiting, couldn't finish updating feed at '%s'\n")
+
+FolderItem *rssyl_feed_subscribe_new(FolderItem *parent, const gchar *url,
+ gboolean verbose);
+
+MsgInfo *rssyl_feed_parse_item_to_msginfo(gchar *file, MsgFlags flags,
+ gboolean a, gboolean b, FolderItem *item);
+
+void rssyl_feed_start_refresh_timeout(RFolderItem *ritem);
+
+#endif /* __RSSYL_FEED_H */
diff --git a/src/plugins/rssyl/rssyl_feed_props.c b/src/plugins/rssyl/rssyl_feed_props.c
new file mode 100644
index 0000000..f6a2d89
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_feed_props.c
@@ -0,0 +1,566 @@
+/*
+ * 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"
+#endif
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+
+/* Claws Mail includes */
+#include <mainwindow.h>
+
+/* Local includes */
+#include "rssyl.h"
+#include "rssyl_feed.h"
+#include "rssyl_feed_props.h"
+#include "rssyl_prefs.h"
+#include "rssyl_update_feed.h"
+
+static void rssyl_gtk_prop_store(RFolderItem *ritem)
+{
+ gchar *url;
+ gint x, old_ri, old_fetch_comments;
+ gboolean use_default_ri = FALSE, keep_old = FALSE;
+ FolderItem *item;
+
+ 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 refresh interval 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;
+
+ /* Set timer for next automatic refresh, if needed. */
+ if( x > 0 ) {
+ if( old_ri != x || ritem->refresh_id == 0 ) {
+ debug_print("RSSyl: GTK - refresh interval changed to %d , updating "
+ "timeout\n", ritem->refresh_interval);
+ rssyl_feed_start_refresh_timeout(ritem);
+ }
+ } else
+ ritem->refresh_id = 0;
+
+ old_fetch_comments = ritem->fetch_comments;
+ ritem->fetch_comments = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(ritem->feedprop->fetch_comments));
+
+ if (!old_fetch_comments && ritem->fetch_comments) {
+ /* reset the RFolderItem's mtime to be sure we get all
+ * available comments */
+ ritem->item.mtime = 0;
+ }
+
+ ritem->fetch_comments_max_age = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(ritem->feedprop->fetch_comments_max_age));
+
+ keep_old = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(ritem->feedprop->keep_old));
+ ritem->keep_old = keep_old;
+
+ ritem->silent_update =
+ gtk_combo_box_get_active(GTK_COMBO_BOX(ritem->feedprop->silent_update));
+
+ ritem->write_heading = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(ritem->feedprop->write_heading));
+
+ ritem->ignore_title_rename = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(ritem->feedprop->ignore_title_rename));
+
+ ritem->ssl_verify_peer = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(ritem->feedprop->ssl_verify_peer));
+
+ /* Store updated properties */
+ item = &ritem->item;
+ item->folder->klass->item_get_xml(item->folder, item);
+}
+
+static gboolean
+rssyl_feedprop_togglebutton_toggled_cb(GtkToggleButton *tb,
+ gpointer data)
+{
+ gboolean active = gtk_toggle_button_get_active(tb);
+ RFeedProp *feedprop = (RFeedProp *)data;
+ GtkWidget *sb = NULL;
+
+ if( (GtkWidget *)tb == feedprop->default_refresh_interval ) {
+ active = !active;
+ sb = feedprop->refresh_interval;
+ } else if( (GtkWidget *)tb == feedprop->fetch_comments ) {
+ sb = feedprop->fetch_comments_max_age;
+ }
+
+ g_return_val_if_fail(sb != NULL, FALSE);
+
+ gtk_widget_set_sensitive(sb, active);
+
+ return FALSE;
+}
+
+
+static gboolean
+rssyl_props_cancel_cb(GtkWidget *widget, gpointer data)
+{
+ RFolderItem *ritem = (RFolderItem *)data;
+
+ debug_print("RSSyl: Cancel pressed\n");
+
+ gtk_widget_destroy(ritem->feedprop->window);
+
+ return FALSE;
+}
+
+static gboolean
+rssyl_props_ok_cb(GtkWidget *widget, gpointer data)
+{
+ RFolderItem *ritem = (RFolderItem *)data;
+
+ debug_print("RSSyl: OK pressed\n");
+ rssyl_gtk_prop_store(ritem);
+
+ gtk_widget_destroy(ritem->feedprop->window);
+
+ return FALSE;
+}
+
+static gboolean
+rssyl_props_trim_cb(GtkWidget *widget, gpointer data)
+{
+ RFolderItem *ritem = (RFolderItem *)data;
+ gboolean k = ritem->keep_old;
+
+ if( k )
+ ritem->keep_old = FALSE;
+
+ rssyl_update_feed(ritem, FALSE);
+
+ if( k )
+ ritem->keep_old = TRUE;
+
+ return FALSE;
+}
+
+static 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;
+}
+
+
+void rssyl_gtk_prop(RFolderItem *ritem)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+ RFeedProp *feedprop;
+ GtkWidget *vbox, *urllabel, *urlframe, *urlalign, *table, *label,
+ *hsep, *sep, *bbox, *cancel_button, *cancel_align,
+ *cancel_hbox, *cancel_image, *cancel_label, *ok_button, *ok_align,
+ *ok_hbox, *ok_image, *ok_label, *trim_button, *silent_update_label;
+ GtkObject *adj;
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ GtkTooltips *tooltips;
+#endif
+ gint refresh;
+ gint row = 0;
+
+ g_return_if_fail(ritem != NULL);
+
+ feedprop = g_new0(RFeedProp, 1);
+
+ /**** Create required widgets ****/
+
+ /* Window */
+ feedprop->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ tooltips = gtk_tooltips_new();
+ gtk_tooltips_enable(tooltips);
+#endif
+
+ /* 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 old items" checkbutton */
+ feedprop->keep_old = gtk_check_button_new_with_mnemonic(
+ _("Keep old items"));
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(feedprop->keep_old),
+ ritem->keep_old);
+
+ /* "Trim" button */
+ trim_button = gtk_button_new_with_mnemonic(_("_Trim"));
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ gtk_tooltips_set_tip(tooltips, trim_button,
+ _("Update feed, deleting items which are no longer in the source feed"), NULL);
+#else
+ gtk_widget_set_tooltip_text(trim_button,
+ _("Update feed, deleting items which are no longer in the source feed"));
+#endif
+
+ 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);
+
+ /* Fetch comments max age spinbutton */
+ adj = gtk_adjustment_new(ritem->fetch_comments_max_age,
+ -1, 100000, 1, 10, 0);
+ feedprop->fetch_comments_max_age = gtk_spin_button_new(GTK_ADJUSTMENT(adj),
+ 1, 0);
+
+ /* Refresh interval spinbutton */
+ adj = gtk_adjustment_new(refresh,
+ 0, 100000, 10, 100, 0);
+ feedprop->refresh_interval = gtk_spin_button_new(GTK_ADJUSTMENT(adj),
+ 1, 0);
+
+ /* 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 new"));
+ 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 new"));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(feedprop->silent_update),
+ ritem->silent_update);
+
+ feedprop->write_heading = gtk_check_button_new_with_mnemonic(
+ _("Add item title to top of message"));
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(feedprop->write_heading),
+ ritem->write_heading);
+
+ /* Ignore title rename - checkbox */
+ feedprop->ignore_title_rename = gtk_check_button_new_with_mnemonic(
+ _("Ignore title rename"));
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(feedprop->ignore_title_rename),
+ ritem->ignore_title_rename);
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ gtk_tooltips_set_tip(tooltips, feedprop->ignore_title_rename,
+ _("Enable this to keep current folder name, even if feed author changes title of the feed."), NULL);
+#else
+ gtk_widget_set_tooltip_text(feedprop->ignore_title_rename,
+ _("Enable this to keep current folder name, even if feed author changes title of the feed."));
+#endif
+
+ /* Verify SSL peer certificate */
+ feedprop->ssl_verify_peer = gtk_check_button_new_with_label(
+ _("Verify SSL certificate validity"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(feedprop->ssl_verify_peer),
+ ritem->ssl_verify_peer);
+
+ /* === Now pack all the widgets */
+ 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(11, 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_feedprop_togglebutton_toggled_cb),
+ (gpointer)feedprop);
+
+ row++;
+ /* Fetch comments max age - label */
+ 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(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 10, 5);
+
+ /* Fetch comments max age - spinbutton */
+ gtk_widget_set_sensitive(feedprop->fetch_comments_max_age,
+ ritem->fetch_comments);
+ gtk_table_attach(GTK_TABLE(table), feedprop->fetch_comments_max_age,
+ 1, 2, row, row+1,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0), 10, 5);
+
+ row++;
+ /* Separator below comments max age */
+ 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++;
+ /* Keep old items - checkbutton */
+ gtk_table_attach(GTK_TABLE(table), feedprop->keep_old,
+ 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 10, 0);
+
+ /* 'Trim' - button */
+ gtk_table_attach(GTK_TABLE(table), trim_button,
+ 1, 2, row, row+1,
+ (GtkAttachOptions) (0),
+ (GtkAttachOptions) (0), 10, 0);
+ g_signal_connect(G_OBJECT(trim_button), "clicked",
+ G_CALLBACK(rssyl_props_trim_cb), ritem);
+
+ 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_feedprop_togglebutton_toggled_cb),
+ (gpointer)feedprop);
+
+ row++;
+ /* Refresh interval - label */
+ 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(label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), 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++;
+ /* Silent update - label */
+ silent_update_label =
+ gtk_label_new(_("<b>If an item changes, do not mark it as new:</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);
+
+ gtk_table_attach(GTK_TABLE(table), feedprop->silent_update, 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++;
+
+ /* Write heading - checkbox */
+ gtk_table_attach(GTK_TABLE(table), feedprop->write_heading,
+ 0, 2, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 10, 0);
+
+
+ row++;
+
+ /* Ignore title rename - checkbutton */
+ gtk_table_attach(GTK_TABLE(table), feedprop->ignore_title_rename,
+ 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 10, 0);
+
+ row++;
+
+ /* Verify SSL peer certificate - checkbutton */
+ gtk_table_attach(GTK_TABLE(table), feedprop->ssl_verify_peer,
+ 0, 1, row, row+1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 10, 0);
+
+ row++;
+
+ /* 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);
+ GTK_WIDGET_SET_FLAGS(ok_button, GTK_CAN_DEFAULT );
+
+ 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;
+}
diff --git a/src/plugins/rssyl/rssyl_feed_props.h b/src/plugins/rssyl/rssyl_feed_props.h
new file mode 100644
index 0000000..5aa4539
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_feed_props.h
@@ -0,0 +1,24 @@
+#ifndef __RSSYL_FEED_PROPS
+#define __RSSYL_FEED_PROPS
+
+#include "rssyl.h"
+
+void rssyl_gtk_prop(RFolderItem *ritem);
+
+struct _RFeedProp {
+ GtkWidget *window;
+ GtkWidget *url;
+ GtkWidget *default_refresh_interval;
+ GtkWidget *refresh_interval;
+ GtkWidget *keep_old;
+ GtkWidget *fetch_comments;
+ GtkWidget *fetch_comments_max_age;
+ GtkWidget *silent_update;
+ GtkWidget *write_heading;
+ GtkWidget *ignore_title_rename;
+ GtkWidget *ssl_verify_peer;
+};
+
+typedef struct _RFeedProp RFeedProp;
+
+#endif /* __RSSYL_FEED_PROPS */
diff --git a/src/plugins/rssyl/rssyl_gtk.c b/src/plugins/rssyl/rssyl_gtk.c
index 24c2abe..dc6e72a 100644
--- a/src/plugins/rssyl/rssyl_gtk.c
+++ b/src/plugins/rssyl/rssyl_gtk.c
@@ -1,5 +1,5 @@
/*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Claws-Mail-- 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>
*
@@ -22,101 +22,85 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
-#include "claws-features.h"
#endif
-#include <glib.h>
-#include <glib/gi18n.h>
-
+/* Global includes */
#include <gtk/gtk.h>
+#include <glib/gi18n.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"
+/* Claws Mail includes */
+#include <gtk/menu.h>
+#include <mainwindow.h>
+#include <inputdialog.h>
+#include <folderview.h>
+#include <alertpanel.h>
+#include <main.h>
+#include <summaryview.h>
-#include "feed.h"
-#include "feedprops.h"
+/* Local includes */
#include "rssyl.h"
-#include "rssyl_cb_gtk.h"
-#include "rssyl_cb_menu.h"
-#include "rssyl_gtk.h"
#include "rssyl_prefs.h"
+#include "rssyl_gtk.h"
+#include "rssyl_cb_menu.h"
static char *rssyl_popup_menu_labels[] =
{
N_("_Refresh feed"),
- N_("Refresh _all feeds"),
+ N_("Feed pr_operties"),
+ N_("Rena_me..."),
+ N_("R_efresh recursively"),
N_("Subscribe _new feed..."),
- N_("_Unsubscribe feed..."),
- N_("Feed pr_operties..."),
+ N_("Create new _folder..."),
N_("Import feed list..."),
- N_("Rena_me..."),
- N_("_Create new folder..."),
N_("_Delete folder..."),
- N_("Remove folder _tree..."),
+ N_("Remove 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) },
-
+ {"FolderViewPopup/RefreshFeed", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_refresh_feed_cb) },
+ {"FolderViewPopup/FeedProperties", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_prop_cb) },
+ {"FolderViewPopup/RenameFolder", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_rename_cb) },
+ {"FolderViewPopup/RefreshAllFeeds", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_update_all_cb) },
+ {"FolderViewPopup/NewFeed", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_feed_cb) },
+ {"FolderViewPopup/NewFolder", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_folder_cb) },
+ {"FolderViewPopup/ImportFeedList", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_import_feed_list_cb) },
+ {"FolderViewPopup/RemoveFolder", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_folder_cb) },
+ {"FolderViewPopup/RemoveMailbox", NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_mailbox_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", "SeparatorRSS1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RefreshAllFeeds", "FolderViewPopup/RefreshAllFeeds", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS2", "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", "NewFolder", "FolderViewPopup/NewFolder", 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", "SeparatorRSS3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
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", "SeparatorRSS4", "FolderView/Popup/---", 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)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS5", "FolderView/Popup/---", 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 );
+ RFolderItem *ritem = (RFolderItem *)item;
+ SET_SENS("FolderViewPopup/RefreshFeed", 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/RefreshAllFeeds", TRUE );
+ SET_SENS("FolderViewPopup/NewFeed", TRUE);
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 );
+ SET_SENS("FolderViewPopup/RemoveFolder", folder_item_parent(item) != NULL);
+ SET_SENS("FolderViewPopup/RemoveMailbox", folder_item_parent(item) == NULL);
#undef SET_SENS
}
@@ -133,20 +117,10 @@ static FolderViewPopup rssyl_popup =
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;
+ gchar *path = NULL, *tmp = NULL;
Folder *folder;
path = input_dialog(_("Add RSS folder tree"),
@@ -160,10 +134,10 @@ static void rssyl_add_mailbox(GtkAction *action, gpointer callback_data)
return;
}
- base = g_path_get_basename(path);
- folder = folder_new(folder_get_class_from_string("rssyl"),
- base, path);
- g_free(base);
+ tmp = g_path_get_basename(path);
+ folder = folder_new(folder_get_class_from_string("rssyl"), tmp, path);
+ g_free(tmp);
+ g_free(path);
if( folder->klass->create_tree(folder) < 0 ) {
alertpanel_error(_("Creation of folder tree failed.\n"
@@ -179,24 +153,30 @@ static void rssyl_add_mailbox(GtkAction *action, gpointer callback_data)
folderview_set(mainwin->folderview);
}
+
static GtkActionEntry mainwindow_add_mailbox[] = {{
"File/AddMailbox/RSSyl",
- NULL, N_("RSSyl..."), NULL, NULL, G_CALLBACK(rssyl_add_mailbox)
+ NULL, "RSSyl...", NULL, NULL, G_CALLBACK(rssyl_add_mailbox)
}};
+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 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)
-
-
+ main_menu_id);
rssyl_fill_popup_menu_labels();
folderview_register_popup(&rssyl_popup);
}
@@ -222,482 +202,5 @@ void rssyl_gtk_done(void)
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;
- 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);
-
- /* 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 has 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);
-
- 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++;
- feedprop->ssl_verify_peer_checkbtn = gtk_check_button_new_with_label(
- _("Verify SSL certificate validity"));
- gtk_widget_show(feedprop->ssl_verify_peer_checkbtn);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(feedprop->ssl_verify_peer_checkbtn),
- ritem->ssl_verify_peer);
- gtk_table_attach(GTK_TABLE(table), feedprop->ssl_verify_peer_checkbtn, 0, 1, row, row+1,
- (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
- (GtkAttachOptions) (0), 10, 5);
-
-
- /* 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;
-
- ritem->ssl_verify_peer = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
- (ritem->feedprop->ssl_verify_peer_checkbtn));;
-
- 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;
}
diff --git a/src/plugins/rssyl/rssyl_gtk.h b/src/plugins/rssyl/rssyl_gtk.h
index a670a6b..6a97fc8 100644
--- a/src/plugins/rssyl/rssyl_gtk.h
+++ b/src/plugins/rssyl/rssyl_gtk.h
@@ -7,27 +7,11 @@
#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;
- GtkWidget *ssl_verify_peer_checkbtn;
-};
-
-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);
+void rssyl_gtk_prop(RFolderItem *ritem);
+
#endif /* __RSSYL_GTK */
diff --git a/src/plugins/rssyl/rssyl_parse_feed.c b/src/plugins/rssyl/rssyl_parse_feed.c
new file mode 100644
index 0000000..88dc406
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_parse_feed.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* Claws Mail includes */
+#include <log.h>
+#include <codeconv.h>
+#include <main.h>
+#include <procmsg.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "libfeed/feeditem.h"
+#include "libfeed/date.h"
+#include "parse822.h"
+#include "rssyl.h"
+#include "rssyl_add_item.h"
+#include "rssyl_deleted.h"
+#include "rssyl_feed.h"
+#include "rssyl_parse_feed.h"
+#include "rssyl_prefs.h"
+#include "strutils.h"
+
+static void rssyl_foreach_parse_func(gpointer data, gpointer user_data)
+{
+ FeedItem *feed_item = (FeedItem *)data;
+ RFolderItem *ritem = (RFolderItem *)user_data;
+
+ rssyl_add_item(ritem, feed_item);
+}
+
+struct _RSSylExpireItemsCtx {
+ gboolean exists;
+ FeedItem *item;
+};
+
+typedef struct _RSSylExpireItemsCtx RSSylExpireItemsCtx;
+
+static void expire_items_func(gpointer data, gpointer user_data)
+{
+ RSSylExpireItemsCtx *ctx = (RSSylExpireItemsCtx *)user_data;
+ FeedItem *item = (FeedItem *)data;
+ gchar *id = NULL, *id2 = NULL;
+
+ if( (id = feed_item_get_id(item)) == NULL )
+ id = feed_item_get_url(item);
+
+ if( id == NULL )
+ return;
+
+ if( (id2 = feed_item_get_id(ctx->item)) == NULL )
+ id2 = feed_item_get_url(ctx->item);
+
+ if( id2 == NULL )
+ return;
+
+ /* Simply check ID, as we should have up-to-date items right now. */
+ if( !strcmp(id, id2) )
+ ctx->exists = TRUE;
+}
+
+static void rssyl_expire_items(RFolderItem *ritem, Feed *feed)
+{
+ FeedItem *item = NULL;
+ GSList *i = NULL;
+ RSSylExpireItemsCtx *ctx = NULL;
+ RFeedCtx *fctx;
+
+ debug_print("RSSyl: rssyl_expire_items()\n");
+
+ g_return_if_fail(ritem != NULL);
+ g_return_if_fail(ritem->items != NULL);
+ g_return_if_fail(feed != NULL);
+
+ ctx = malloc( sizeof(RSSylExpireItemsCtx) );
+
+ /* Check each locally stored item, if it is still in the upstream
+ * feed - xnay it if not. */
+ for( i = ritem->items; i != NULL; i = i->next ) {
+ item = (FeedItem *)i->data;
+
+ /* Do not expire comments, they expire with their parents */
+ if (feed_item_get_parent_id(item) != NULL)
+ continue;
+
+ ctx->exists = FALSE;
+ ctx->item = item;
+ feed_foreach_item(feed, expire_items_func, ctx);
+
+ if( !ctx->exists ) {
+ fctx = (RFeedCtx *)item->data;
+ /* TODO: expire item's comments (items with our parent_id) */
+ g_remove(fctx->path);
+ }
+ }
+
+ g_free(ctx);
+}
+
+/* -------------------------------------------------------------------------
+ * rssyl_parse_feed() */
+
+gboolean rssyl_parse_feed(RFolderItem *ritem, Feed *feed)
+{
+ gchar *tmp = NULL, *tmp2 = NULL;
+ gint i = 1;
+
+ g_return_val_if_fail(ritem != NULL, FALSE);
+ g_return_val_if_fail(feed != NULL, FALSE);
+ g_return_val_if_fail(feed->title != NULL, FALSE);
+
+ debug_print("RSSyl: parse_feed\n");
+
+ /* Set the last_update timestamp here, so it is the same for all items */
+ ritem->last_update = time(NULL);
+
+ /* If the upstream feed changed its title, change name of our folder
+ * accordingly even if user has renamed it before. This makes sure that
+ * user will be aware of the upstream title change. */
+ if( !ritem->ignore_title_rename &&
+ (ritem->official_title == NULL ||
+ strcmp(feed->title, ritem->official_title)) ) {
+ g_free(ritem->official_title);
+ ritem->official_title = g_strdup(feed->title);
+
+ tmp = rssyl_format_string(feed->title, TRUE, TRUE);
+
+ tmp2 = g_strdup(tmp);
+ while (folder_item_rename(&ritem->item, tmp2) != 0 && i < 20) {
+ g_free(tmp2);
+ tmp2 = g_strdup_printf("%s__%d", tmp, ++i);
+ debug_print("RSSyl: couldn't rename, trying '%s'\n", tmp2);
+ }
+ /* TODO: handle case when i reaches 20 */
+
+ g_free(tmp);
+ g_free(tmp2);
+
+ /* FIXME: update name in properties */
+ /* FIXME: store feed properties */
+ }
+
+ folder_item_update_freeze();
+
+ /* Read contents of folder, so we can check for duplicates/updates */
+ rssyl_folder_read_existing(ritem);
+
+ if( claws_is_exiting() ) {
+ debug_print("RSSyl: Claws-Mail is exiting, bailing out\n");
+ log_print(LOG_PROTOCOL, RSSYL_LOG_ABORTED_EXITING, ritem->url);
+ folder_item_update_thaw();
+ return TRUE;
+ }
+
+ /* Populate the ->deleted_items list so that we can check it when
+ * adding each item. */
+ ritem->deleted_items = rssyl_deleted_update(ritem);
+
+ /* Parse each item in the feed, adding or updating existing items if
+ * necessary */
+ if( feed_n_items(feed) > 0 )
+ feed_foreach_item(feed, rssyl_foreach_parse_func, (gpointer)ritem);
+
+ if( !ritem->keep_old && !ritem->fetching_comments ) {
+ rssyl_folder_read_existing(ritem);
+ rssyl_expire_items(ritem, feed);
+ }
+
+ rssyl_deleted_free(ritem->deleted_items);
+
+ folder_item_scan(&ritem->item);
+ folder_item_update_thaw();
+
+ if( !ritem->fetching_comments )
+ log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATED, ritem->url);
+
+ return TRUE;
+}
diff --git a/src/plugins/rssyl/rssyl_parse_feed.h b/src/plugins/rssyl/rssyl_parse_feed.h
new file mode 100644
index 0000000..5e4a3f3
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_parse_feed.h
@@ -0,0 +1,16 @@
+#ifndef __RSSYL_PARSE_FEED
+#define __RSSYL_PARSE_FEED
+
+#include <glib.h>
+
+#include "rssyl.h"
+#include "libfeed/feed.h"
+
+#define RSSYL_TMP_TEMPLATE "rssylXXXXXX"
+
+#define RSSYL_TEXT_START "<!-- RSSyl text start -->"
+#define RSSYL_TEXT_END "<!-- RSSyl text end -->"
+
+gboolean rssyl_parse_feed(RFolderItem *ritem, Feed *feed);
+
+#endif /* __RSSYL_PARSE_FEED */
diff --git a/src/plugins/rssyl/rssyl_prefs.c b/src/plugins/rssyl/rssyl_prefs.c
index 97b2b83..6b8b050 100644
--- a/src/plugins/rssyl/rssyl_prefs.c
+++ b/src/plugins/rssyl/rssyl_prefs.c
@@ -22,39 +22,43 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
-#include "claws-features.h"
#endif
+/* Global includes */
#include <glib.h>
#include <glib/gi18n.h>
-#include "common/defs.h"
-#include "common/utils.h"
-#include "gtk/gtkutils.h"
-#include "prefs_gtk.h"
+/* Claws Mail includes */
+#include <common/defs.h>
+#include <prefs_gtk.h>
+#include <mainwindow.h>
+/* Local includes */
+#include "rssyl.h"
#include "rssyl_prefs.h"
+#include "rssyl_feed.h"
-static RSSylPrefsPage rssyl_prefs_page;
-RSSylPrefs rssyl_prefs;
+static RPrefsPage rssyl_gtk_prefs_page;
+RPrefs 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 },
- { "ssl_verify_peer", "TRUE", &rssyl_prefs.ssl_verify_peer, P_BOOL,
- NULL, NULL, NULL },
- { 0, 0, 0, 0, 0, 0, 0 }
+static void rssyl_apply_prefs(void);
+
+ static PrefParam param[] = {
+ { "refresh_interval", PREF_DEFAULT_REFRESH, &rssyl_prefs.refresh, P_INT,
+ NULL, NULL, NULL },
+ { "refresh_on_startup", "FALSE", &rssyl_prefs.refresh_on_startup, P_BOOL,
+ NULL, NULL, NULL },
+ { "refresh_enabled", "TRUE", &rssyl_prefs.refresh_enabled, P_BOOL,
+ NULL, NULL, NULL },
+ { "cookies_path", "", &rssyl_prefs.cookies_path, P_STRING,
+ NULL, NULL, NULL },
+ { "ssl_verify_peer", "TRUE", &rssyl_prefs.ssl_verify_peer, P_BOOL,
+ NULL, NULL, NULL },
+ { 0, 0, 0, 0, 0, 0, 0 }
};
void rssyl_prefs_init(void)
@@ -71,98 +75,119 @@ void rssyl_prefs_init(void)
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;
+ rssyl_gtk_prefs_page.page.path = path;
+ rssyl_gtk_prefs_page.page.create_widget = create_rssyl_prefs_page;
+ rssyl_gtk_prefs_page.page.destroy_widget = destroy_rssyl_prefs_page;
+ rssyl_gtk_prefs_page.page.save_page = save_rssyl_prefs;
+ rssyl_gtk_prefs_page.page.weight = 30.0;
- prefs_gtk_register_page((PrefsPage *) &rssyl_prefs_page);
+ prefs_gtk_register_page((PrefsPage *) &rssyl_gtk_prefs_page);
}
void rssyl_prefs_done(void)
{
- prefs_gtk_unregister_page((PrefsPage *) &rssyl_prefs_page);
+ prefs_gtk_unregister_page((PrefsPage *) &rssyl_gtk_prefs_page);
+}
+
+/* Toggle the refresh timeout spinbutton sensitivity after the
+ * checkbutton was toggled. */
+static gboolean
+rssyl_refresh_enabled_toggled_cb(GtkToggleButton *tb, gpointer data)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(data),
+ gtk_toggle_button_get_active(tb));
+ return FALSE;
}
static void create_rssyl_prefs_page(PrefsPage *page,
GtkWindow *window, gpointer data)
{
- RSSylPrefsPage *prefs_page = (RSSylPrefsPage *) page;
+ int row = 0;
+ RPrefsPage *prefs_page = (RPrefsPage *) page;
GtkWidget *table;
- GtkWidget *refresh;
- GtkWidget *expired;
+ GtkWidget *refresh, *refresh_enabled;
+ GtkWidget *label;
GtkWidget *refresh_on_startup;
+ GtkObject *refresh_adj;
GtkWidget *cookies_path;
- GtkWidget *ssl_verify_peer_checkbtn;
- GtkWidget *label;
- GtkObject *refresh_adj, *expired_adj;
+ GtkWidget *ssl_verify_peer;
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ GtkTooltips *tooltips;
+
+ tooltips = gtk_tooltips_new();
+ gtk_tooltips_enable(tooltips);
+#endif
- table = gtk_table_new(RSSYL_NUM_PREFS, 2, FALSE);
- gtk_container_set_border_width(GTK_CONTAINER(table), 6);
+ table = gtk_table_new(3, 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 interval */
+ refresh_enabled = gtk_check_button_new_with_label(
+ _("Default refresh interval in minutes"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(refresh_enabled),
+ rssyl_prefs.refresh_enabled);
+ gtk_table_attach(GTK_TABLE(table), refresh_enabled, 0, 1, row, row+1,
+ GTK_FILL | GTK_EXPAND, 0, 0, 0);
refresh_adj = gtk_adjustment_new(rssyl_prefs.refresh,
- 0, 100000, 1, 10, 0);
+ 1, 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_widget_set_sensitive(GTK_WIDGET(refresh), rssyl_prefs.refresh_enabled);
+ gtk_table_attach(GTK_TABLE(table), refresh, 1, 2, row, row+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"));
+ g_signal_connect(G_OBJECT(refresh_enabled), "toggled",
+ G_CALLBACK(rssyl_refresh_enabled_toggled_cb), refresh);
+ row++;
+ /* Whether to refresh all feeds on CM startup */
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_table_attach(GTK_TABLE(table), refresh_on_startup, 0, 2, row, row+1,
GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ row++;
+ /* Path to cookies file for libcurl to use */
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_table_attach(GTK_TABLE(table), label, 0, 1, row, row+1,
+ GTK_FILL | GTK_EXPAND, 0, 0, 0);
gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
- cookies_path = gtk_entry_new ();
+ 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,
+ gtk_table_attach(GTK_TABLE(table), cookies_path, 1, 2, row, row+1,
+ GTK_FILL, 0, 0, 0);
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ gtk_tooltips_set_tip(tooltips, cookies_path,
+ _("Path to Netscape-style cookies.txt file containing your cookies"), NULL);
+#else
+ gtk_widget_set_tooltip_text(cookies_path,
_("Path to Netscape-style cookies.txt file containing your cookies"));
+#endif
- ssl_verify_peer_checkbtn = gtk_check_button_new_with_label(
- _("Verify SSL certificate validity for new feeds"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ssl_verify_peer_checkbtn),
+ row++;
+
+ /* Whether to verify SSL peer certificate */
+ ssl_verify_peer = gtk_check_button_new_with_label(
+ _("Verify SSL certificates validity for new feeds"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ssl_verify_peer),
rssyl_prefs.ssl_verify_peer);
- gtk_table_attach(GTK_TABLE(table), ssl_verify_peer_checkbtn, 0, 2, 5, 6,
+ gtk_table_attach(GTK_TABLE(table), ssl_verify_peer, 0, 2, row, row+1,
GTK_FILL | GTK_EXPAND, 0, 0, 0);
gtk_widget_show_all(table);
+ /* Store pointers to relevant widgets */
prefs_page->page.widget = table;
+ prefs_page->refresh_enabled = refresh_enabled;
prefs_page->refresh = refresh;
- prefs_page->expired = expired;
prefs_page->refresh_on_startup = refresh_on_startup;
prefs_page->cookies_path = cookies_path;
- prefs_page->ssl_verify_peer_checkbtn = ssl_verify_peer_checkbtn;
+ prefs_page->ssl_verify_peer = ssl_verify_peer;
}
static void destroy_rssyl_prefs_page(PrefsPage *page)
@@ -172,23 +197,25 @@ static void destroy_rssyl_prefs_page(PrefsPage *page)
static void save_rssyl_prefs(PrefsPage *page)
{
- RSSylPrefsPage *prefs_page = (RSSylPrefsPage *)page;
+ RPrefsPage *prefs_page = (RPrefsPage *)page;
PrefFile *pref_file;
gchar *rc_file_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
COMMON_RC, NULL);
+ /* Grab values from GTK widgets */
+ rssyl_prefs.refresh_enabled = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(prefs_page->refresh_enabled));
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)));
+ GTK_ENTRY(prefs_page->cookies_path)));
rssyl_prefs.ssl_verify_peer = gtk_toggle_button_get_active(
- GTK_TOGGLE_BUTTON(prefs_page->ssl_verify_peer_checkbtn));
+ GTK_TOGGLE_BUTTON(prefs_page->ssl_verify_peer));
+ /* Store prefs in rc file */
pref_file = prefs_write_open(rc_file_path);
g_free(rc_file_path);
@@ -201,14 +228,56 @@ static void save_rssyl_prefs(PrefsPage *page)
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);
+ fprintf(pref_file->fp, "\n");
+ prefs_file_close(pref_file);
+
+ rssyl_apply_prefs();
}
-RSSylPrefs *rssyl_prefs_get(void)
+RPrefs *rssyl_prefs_get(void)
{
return &rssyl_prefs;
}
+
+static void rssyl_start_default_refresh_timeouts_func(FolderItem *item,
+ gpointer data)
+{
+ RFolderItem *ritem = (RFolderItem *)item;
+ guint prefs_interval = GPOINTER_TO_UINT(data);
+
+ if( !IS_RSSYL_FOLDER_ITEM(item) )
+ return;
+
+ if( folder_item_parent(item) == NULL || ritem->url == NULL )
+ return;
+
+ /* Feeds which use default refresh interval */
+ if( ritem->default_refresh_interval ) {
+ /* Start a new timer if the default value has changed
+ * (ritem->refresh_interval should contain previous default
+ * value in this case). */
+ if( ritem->refresh_interval != prefs_interval ) {
+ ritem->refresh_interval = prefs_interval;
+ rssyl_feed_start_refresh_timeout(ritem);
+ }
+ }
+}
+
+static void rssyl_start_default_refresh_timeouts(void)
+{
+ RPrefs *rsprefs = rssyl_prefs_get();
+
+ folder_func_to_all_folders(
+ (FolderItemFunc)rssyl_start_default_refresh_timeouts_func,
+ GUINT_TO_POINTER(rsprefs->refresh));
+}
+
+/* rssyl_apply_prefs():
+ * Do what's needed to start using newly set preferences */
+static void rssyl_apply_prefs(void)
+{
+ /* Update refresh timeouts for feeds which use default interval. */
+ rssyl_start_default_refresh_timeouts();
+
+ /* Nothing else here, so far... */
+}
diff --git a/src/plugins/rssyl/rssyl_prefs.h b/src/plugins/rssyl/rssyl_prefs.h
index 15248ce..51325f6 100644
--- a/src/plugins/rssyl/rssyl_prefs.h
+++ b/src/plugins/rssyl/rssyl_prefs.h
@@ -3,34 +3,31 @@
#define PREFS_BLOCK_NAME "rssyl"
-#define RSSYL_NUM_PREFS 4
+#define PREF_DEFAULT_REFRESH "180"
-#define RSSYL_PREF_DEFAULT_REFRESH "180"
-#define RSSYL_PREF_DEFAULT_EXPIRED "-1"
+typedef struct _RPrefs RPrefs;
-typedef struct _RSSylPrefs RSSylPrefs;
-
-struct _RSSylPrefs {
+struct _RPrefs {
+ gboolean refresh_enabled;
gint refresh;
- gint expired;
gboolean refresh_on_startup;
gchar *cookies_path;
gboolean ssl_verify_peer;
};
-typedef struct _RSSylPrefsPage RSSylPrefsPage;
+typedef struct _RPrefsPage RPrefsPage;
-struct _RSSylPrefsPage {
+struct _RPrefsPage {
PrefsPage page;
+ GtkWidget *refresh_enabled;
GtkWidget *refresh;
- GtkWidget *expired;
GtkWidget *refresh_on_startup;
GtkWidget *cookies_path;
- GtkWidget *ssl_verify_peer_checkbtn;
+ GtkWidget *ssl_verify_peer;
};
void rssyl_prefs_init(void);
void rssyl_prefs_done(void);
-RSSylPrefs *rssyl_prefs_get(void);
+RPrefs *rssyl_prefs_get(void);
#endif /* __RSSYL_PREFS */
diff --git a/src/plugins/rssyl/rssyl_subscribe.c b/src/plugins/rssyl/rssyl_subscribe.c
new file mode 100644
index 0000000..3bdbc73
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_subscribe.c
@@ -0,0 +1,151 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * - DESCRIPTION HERE
+ *
+ * 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
+
+/* Global includes */
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+/* Claws Mail includes */
+#include <alertpanel.h>
+#include <folder.h>
+#include <log.h>
+#include <common/utils.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "libfeed/feeditem.h"
+#include "rssyl.h"
+#include "rssyl_add_item.h"
+#include "rssyl_feed.h"
+#include "rssyl_update_feed.h"
+#include "rssyl_subscribe_gtk.h"
+#include "strutils.h"
+
+static void rssyl_subscribe_foreach_func(gpointer data, gpointer user_data)
+{
+ RFolderItem *ritem = (RFolderItem *)user_data;
+ FeedItem *feed_item = (FeedItem *)data;
+
+ g_return_if_fail(ritem != NULL);
+ g_return_if_fail(feed_item != NULL);
+
+ rssyl_add_item(ritem, feed_item);
+}
+
+gboolean rssyl_subscribe(FolderItem *parent, const gchar *url,
+ gboolean verbose)
+{
+ gchar *myurl = NULL, *tmpname = NULL, *tmpname2 = NULL;
+ RFetchCtx *ctx;
+ FolderItem *new_item;
+ RFolderItem *ritem;
+ gint i = 1;
+ RSubCtx *sctx;
+
+ g_return_val_if_fail(parent != NULL, FALSE);
+ g_return_val_if_fail(url != NULL, FALSE);
+
+ log_print(LOG_PROTOCOL, RSSYL_LOG_SUBSCRIBING, url);
+
+ myurl = my_normalize_url(url);
+
+ /* Fetch the feed. */
+ ctx = rssyl_prep_fetchctx_from_url(myurl);
+ g_free(myurl);
+ g_return_val_if_fail(ctx != NULL, FALSE);
+
+ rssyl_fetch_feed(ctx, verbose);
+
+ debug_print("RSSyl: fetch success == %s\n",
+ ctx->success ? "TRUE" : "FALSE");
+
+ if (!ctx->success) {
+ /* User notification was already handled inside rssyl_fetch_feed(),
+ * let's just return quietly. */
+ feed_free(ctx->feed);
+ g_free(ctx->error);
+ g_free(ctx);
+ return FALSE;
+ }
+
+ if (verbose) {
+ sctx = g_new0(RSubCtx, 1);
+ sctx->feed = ctx->feed;
+
+ debug_print("RSSyl: Calling subscribe dialog routine...\n");
+ rssyl_subscribe_dialog(sctx);
+
+ if (sctx->feed == NULL) {
+ debug_print("RSSyl: User cancelled subscribe.\n");
+ g_free(sctx);
+ return FALSE;
+ }
+ }
+
+ /* OK, feed is succesfully fetched and correct, let's add it to CM. */
+
+ /* Create a folder for it. */
+ tmpname = rssyl_format_string(ctx->feed->title, TRUE, TRUE);
+ tmpname2 = g_strdup(tmpname);
+ while (folder_find_child_item_by_name(parent, tmpname2) != 0 && i < 20) {
+ debug_print("RSSyl: Folder '%s' already exists, trying another name\n",
+ tmpname2);
+ g_free(tmpname2);
+ tmpname2 = g_strdup_printf("%s__%d", tmpname, ++i);
+ }
+ /* TODO: handle cases where i reaches 20 */
+
+ folder_item_update_freeze();
+
+ new_item = folder_create_folder(parent, tmpname2);
+ g_free(tmpname);
+ g_free(tmpname2);
+
+ if (!new_item) {
+ if (verbose)
+ alertpanel_error(_("Couldn't create folder for new feed '%s'."),
+ myurl);
+ feed_free(ctx->feed);
+ g_free(ctx->error);
+ g_free(ctx);
+ g_free(myurl);
+ return FALSE;
+ }
+
+ debug_print("RSSyl: Adding '%s'\n", ctx->feed->url);
+
+ ritem = (RFolderItem *)new_item;
+ ritem->url = g_strdup(ctx->feed->url);
+
+ if (feed_n_items(ctx->feed) > 0)
+ feed_foreach_item(ctx->feed, rssyl_subscribe_foreach_func, (gpointer)ritem);
+
+ folder_item_scan(new_item);
+ folder_write_list();
+ folder_item_update_thaw();
+
+ return TRUE;
+}
diff --git a/src/plugins/rssyl/rssyl_subscribe.h b/src/plugins/rssyl/rssyl_subscribe.h
new file mode 100644
index 0000000..52b80f9
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_subscribe.h
@@ -0,0 +1,6 @@
+#ifndef __RSSYL_SUBSCRIBE_H
+#define __RSSYL_SUBSCRIBE_H
+
+gboolean rssyl_subscribe(FolderItem *parent, const gchar *url, gboolean verbose);
+
+#endif /* __RSSYL_SUBSCRIBE_H */
diff --git a/src/plugins/rssyl/rssyl_subscribe_gtk.c b/src/plugins/rssyl/rssyl_subscribe_gtk.c
new file mode 100644
index 0000000..3f63440
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_subscribe_gtk.c
@@ -0,0 +1,109 @@
+/*
+ * Claws-Mail-- 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>
+ *
+ * - Dialog which appears when manually subscribing a new feed.
+ *
+ * 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
+
+/* Global includes */
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+/* Claws Mail includes */
+#include <mainwindow.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "rssyl_subscribe_gtk.h"
+
+void rssyl_subscribe_dialog(RSubCtx *ctx) {
+ GtkWidget *win, *vbox, *titleframe, *titlelabel;
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ GtkTooltips *tooltips;
+#endif
+ gint ret;
+ gchar *newtitle;
+
+ g_return_if_fail(ctx != NULL);
+ g_return_if_fail(ctx->feed != NULL);
+
+ /* Create window */
+ win = gtk_dialog_new_with_buttons(_("Subscribe new feed?"),
+ GTK_WINDOW(mainwindow_get_mainwindow()->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(win), GTK_RESPONSE_ACCEPT);
+
+ vbox = gtk_dialog_get_content_area(GTK_DIALOG(win));
+
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ tooltips = gtk_tooltips_new();
+ gtk_tooltips_enable(tooltips);
+#endif
+
+ /* Feed title */
+ titleframe = gtk_frame_new(NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(titleframe), 5);
+ gtk_frame_set_label_align(GTK_FRAME(titleframe), 0.05, 0.5);
+ gtk_frame_set_shadow_type(GTK_FRAME(titleframe), GTK_SHADOW_ETCHED_OUT);
+ gtk_box_pack_start(GTK_BOX(vbox), titleframe, FALSE, FALSE, 0);
+
+ titlelabel = gtk_label_new(_("<b>Feed folder:</b>"));
+ gtk_label_set_use_markup(GTK_LABEL(titlelabel), TRUE);
+ gtk_misc_set_padding(GTK_MISC(titlelabel), 5, 0);
+ gtk_frame_set_label_widget(GTK_FRAME(titleframe), titlelabel);
+
+ ctx->title = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(ctx->title), feed_get_title(ctx->feed));
+ gtk_entry_set_activates_default(GTK_ENTRY(ctx->title), TRUE);
+#if !(GTK_CHECK_VERSION(2, 12, 0))
+ gtk_tooltips_set_tip(tooltips, ctx->title,
+ _("Instead of using official title, you can enter a different folder "
+ "name for the feed."), NULL);
+#else
+ gtk_widget_set_tooltip_text(ctx->title,
+ _("Instead of using official title, you can enter a different folder "
+ "name for the feed."));
+#endif
+ gtk_container_add(GTK_CONTAINER(titleframe), ctx->title);
+
+ gtk_widget_show_all(vbox);
+
+ ret = gtk_dialog_run(GTK_DIALOG(win));
+
+ if (ret == GTK_RESPONSE_ACCEPT) {
+ /* Modify ctx->feed based on user changes in dialog */
+ newtitle = (gchar *)gtk_entry_get_text(GTK_ENTRY(ctx->title));
+ if (strcmp(feed_get_title(ctx->feed), newtitle)) {
+ debug_print("RSSyl: Using feed title '%s'\n", newtitle);
+ feed_set_title(ctx->feed, newtitle);
+ }
+ } else {
+ /* Destroy the feed to signal outside that user cancelled subscribing */
+ feed_free(ctx->feed);
+ ctx->feed = NULL;
+ }
+
+ gtk_widget_destroy(win);
+}
diff --git a/src/plugins/rssyl/rssyl_subscribe_gtk.h b/src/plugins/rssyl/rssyl_subscribe_gtk.h
new file mode 100644
index 0000000..2d40b13
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_subscribe_gtk.h
@@ -0,0 +1,15 @@
+#ifndef __RSSYL_SUBSCRIBE_GTK_H
+#define __RSSYL_SUBSCRIBE_GTK_H
+
+#include "libfeed/feed.h"
+
+struct _RSubCtx {
+ Feed *feed;
+ GtkWidget *title;
+};
+
+typedef struct _RSubCtx RSubCtx;
+
+void rssyl_subscribe_dialog(RSubCtx *ctx);
+
+#endif /* __RSSYL_SUBSCRIBE_GTK_H */
diff --git a/src/plugins/rssyl/rssyl_update_comments.c b/src/plugins/rssyl/rssyl_update_comments.c
new file mode 100644
index 0000000..c54142d
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_comments.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <pthread.h>
+
+/* Claws Mail includes */
+#include <log.h>
+#include <mainwindow.h>
+#include <statusbar.h>
+#include <main.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "rssyl.h"
+#include "rssyl_feed.h"
+#include "rssyl_parse_feed.h"
+#include "rssyl_update_feed.h"
+#include "parse822.h"
+
+static void rssyl_update_reference_func(gpointer data, gpointer user_data)
+{
+ FeedItem *item = (FeedItem *)data;
+ gchar *parent_id = (gchar *)user_data;
+
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(user_data != NULL);
+
+ feed_item_set_parent_id(item, parent_id);
+}
+
+void rssyl_update_comments(RFolderItem *ritem)
+{
+ FolderItem *item = &ritem->item;
+ FeedItem *fi = NULL;
+ RFetchCtx *ctx = NULL;
+ DIR *dp;
+ struct dirent *d;
+ gint num;
+ gchar *path, *msg, *fname;
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ g_return_if_fail(ritem != NULL);
+
+ if( ritem->fetch_comments == FALSE )
+ return;
+
+ path = folder_item_get_path(item);
+ g_return_if_fail(path != NULL);
+
+ debug_print("RSSyl: starting to parse comments, path is '%s'\n", path);
+
+ if( (dp = opendir(path)) == NULL ) {
+ FILE_OP_ERROR(item->path, "opendir");
+ g_free(path);
+ return;
+ }
+
+ ritem->fetching_comments = TRUE;
+
+ while( (d = readdir(dp)) != NULL ) {
+ if (claws_is_exiting()) {
+ closedir(dp);
+ g_free(path);
+ debug_print("RSSyl: bailing out, app is exiting\n");
+ }
+
+ if( (num = to_number(d->d_name)) > 0 && d->d_type == DT_REG ) {
+ debug_print("RSSyl: starting to parse '%s'\n", d->d_name);
+
+ fname = g_strdup_printf("%s%c%s", path, G_DIR_SEPARATOR, d->d_name);
+ if( (fi = rssyl_parse_folder_item_file(fname)) != NULL ) {
+ if( feed_item_get_comments_url(fi) && feed_item_get_id(fi) &&
+ (ritem->fetch_comments_max_age == -1 ||
+ time(NULL) - feed_item_get_date_modified(fi) <= ritem->fetch_comments_max_age*86400)) {
+ msg = g_strdup_printf(_("Updating comments for '%s'..."),
+ feed_item_get_title(fi));
+ debug_print("RSSyl: updating comments for '%s' (%s)\n",
+ feed_item_get_title(fi), feed_item_get_comments_url(fi));
+ STATUSBAR_PUSH(mainwin, msg);
+
+ ctx = rssyl_prep_fetchctx_from_url(feed_item_get_comments_url(fi));
+ g_return_if_fail(ctx != NULL);
+ feed_set_ssl_verify_peer(ctx->feed, ritem->ssl_verify_peer);
+
+ rssyl_fetch_feed(ctx, FALSE);
+
+ if( ctx->success && feed_n_items(ctx->feed) > 0 ) {
+ g_free(ctx->feed->title);
+ ctx->feed->title = g_strdup(ritem->official_title);
+
+ feed_foreach_item(ctx->feed, rssyl_update_reference_func,
+ feed_item_get_id(fi));
+
+ if( !rssyl_parse_feed(ritem, ctx->feed) ) {
+ debug_print("RSSyl: Error processing comments feed\n");
+ log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PROC, ctx->feed->url);
+ }
+ }
+ }
+
+ STATUSBAR_POP(mainwin);
+
+ feed_item_free(fi);
+ }
+
+ g_free(fname);
+ }
+ }
+
+ closedir(dp);
+ g_free(path);
+
+ ritem->fetching_comments = FALSE;
+
+ debug_print("RSSyl: rssyl_update_comments() is done\n");
+}
diff --git a/src/plugins/rssyl/rssyl_update_comments.h b/src/plugins/rssyl/rssyl_update_comments.h
new file mode 100644
index 0000000..3ce56e4
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_comments.h
@@ -0,0 +1,12 @@
+
+
+#ifndef __RSSYL_UPDATE_COMMENTS
+#define __RSSYL_UPDATE_COMMENTS
+
+#include <glib.h>
+
+#include "rssyl.h"
+
+void rssyl_update_comments(RFolderItem *ritem);
+
+#endif /* __RSSYL_UPDATE_COMMENTS */
diff --git a/src/plugins/rssyl/rssyl_update_feed.c b/src/plugins/rssyl/rssyl_update_feed.c
new file mode 100644
index 0000000..8a97f90
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_feed.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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
+
+/* Global includes */
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <pthread.h>
+
+/* Claws Mail includes */
+#include <common/claws.h>
+#include <mainwindow.h>
+#include <statusbar.h>
+#include <alertpanel.h>
+#include <log.h>
+#include <prefs_common.h>
+#include <inc.h>
+#include <main.h>
+
+/* Local includes */
+#include "libfeed/feed.h"
+#include "rssyl.h"
+#include "rssyl_deleted.h"
+#include "rssyl_feed.h"
+#include "rssyl_parse_feed.h"
+#include "rssyl_prefs.h"
+#include "rssyl_update_comments.h"
+
+/* rssyl_fetch_feed_thr() */
+
+static void *rssyl_fetch_feed_thr(void *arg)
+{
+ RFetchCtx *ctx = (RFetchCtx *)arg;
+
+ /* Fetch and parse the feed. */
+ ctx->response_code = feed_update(ctx->feed, -1);
+
+ /* Signal main thread that we're done here. */
+ ctx->ready = TRUE;
+
+ return NULL;
+}
+
+/* rssyl_fetch_feed() */
+RFetchCtx *rssyl_fetch_feed(RFetchCtx *ctx, gboolean verbose)
+{
+#ifdef USE_PTHREAD
+ pthread_t pt;
+#endif
+
+ g_return_if_fail(ctx != NULL);
+
+#ifdef USE_PTHREAD
+ if( pthread_create(&pt, PTHREAD_CREATE_JOINABLE, rssyl_fetch_feed_thr,
+ (void *)ctx) != 0 ) {
+ /* Bummer, couldn't create thread. Continue non-threaded. */
+ rssyl_fetch_feed_thr(ctx);
+ } else {
+ /* Thread created, let's wait until it finishes. */
+ debug_print("RSSyl: waiting for thread to finish (timeout: %ds)\n",
+ feed_get_timeout(ctx->feed));
+ while( !ctx->ready ) {
+ claws_do_idle();
+ }
+
+ debug_print("RSSyl: thread finished\n");
+ pthread_join(pt, NULL);
+ }
+#else
+ debug_print("RSSyl: no pthreads available, running non-threaded fetch\n");
+ rssyl_fetch_feed_thr(ctx);
+#endif
+
+ if( ctx->response_code == FEED_ERR_INIT ) {
+ debug_print("RSSyl: libfeed reports init error from libcurl\n");
+ ctx->error = g_strdup("Internal error");
+ } else if( ctx->response_code == FEED_ERR_FETCH ) {
+ debug_print("RSSyl: libfeed reports some other error from libcurl\n");
+ ctx->error = g_strdup(ctx->feed->fetcherr);
+ } else if( ctx->response_code >= 400 && ctx->response_code < 500 ) {
+ switch( ctx->response_code ) {
+ case 401:
+ ctx->error = g_strdup(_("401 (Authorisation required)"));
+ break;
+ case 403:
+ ctx->error = g_strdup(_("403 (Unauthorised)"));
+ break;
+ case 404:
+ ctx->error = g_strdup(_("404 (Not found)"));
+ break;
+ default:
+ ctx->error = g_strdup_printf(_("Error %d"), ctx->response_code);
+ break;
+ }
+ }
+
+ /* Here we handle "imperfect" conditions. If verbose is TRUE, we also
+ * display error dialogs for user. We always log the error. */
+ if( ctx->error != NULL ) {
+ /* libcurl wasn't happy */
+ debug_print("RSSyl: Error: %s\n", ctx->error);
+ if( verbose )
+ alertpanel_error(C_("First parameter is URL, second is error text",
+ "Error fetching feed at\n<b>%s</b>:\n\n%s"),
+ feed_get_url(ctx->feed), ctx->error);
+
+ log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_FETCH, ctx->feed->url, ctx->error);
+
+ ctx->success = FALSE;
+ } else {
+ if( ctx->feed == NULL || feed_get_title(ctx->feed) == NULL ) {
+ /* libcurl was happy, but libfeed wasn't */
+ debug_print("RSSyl: Error reading feed\n");
+ if( verbose )
+ alertpanel_error(_("No valid feed found at\n<b>%s</b>"),
+ feed_get_url(ctx->feed));
+ log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_NOFEED, ctx->feed->url);
+ ctx->success = FALSE;
+ }
+ }
+}
+
+RFetchCtx *rssyl_prep_fetchctx_from_item(RFolderItem *ritem)
+{
+ RFetchCtx *ctx = NULL;
+
+ g_return_val_if_fail(ritem != NULL, NULL);
+
+ ctx = g_new0(RFetchCtx, 1);
+ ctx->feed = feed_new(ritem->url);
+ ctx->error = NULL;
+ ctx->success = TRUE;
+ ctx->ready = FALSE;
+
+ feed_set_timeout(ctx->feed, prefs_common.io_timeout_secs);
+ feed_set_cookies_path(ctx->feed, rssyl_prefs_get()->cookies_path);
+ feed_set_ssl_verify_peer(ctx->feed, ritem->ssl_verify_peer);
+
+ return ctx;
+}
+
+RFetchCtx *rssyl_prep_fetchctx_from_url(gchar *url)
+{
+ RFetchCtx *ctx = NULL;
+
+ g_return_val_if_fail(url != NULL, NULL);
+
+ ctx = g_new0(RFetchCtx, 1);
+ ctx->feed = feed_new(url);
+ ctx->error = NULL;
+ ctx->success = TRUE;
+ ctx->ready = FALSE;
+
+ feed_set_timeout(ctx->feed, prefs_common.io_timeout_secs);
+ feed_set_cookies_path(ctx->feed, rssyl_prefs_get()->cookies_path);
+ feed_set_ssl_verify_peer(ctx->feed, rssyl_prefs_get()->ssl_verify_peer);
+
+ return ctx;
+}
+
+/* rssyl_update_feed() */
+
+gboolean rssyl_update_feed(RFolderItem *ritem, gboolean verbose)
+{
+ RFetchCtx *ctx = NULL;
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+ gchar *msg = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail(ritem != NULL, FALSE);
+ g_return_val_if_fail(ritem->url != NULL, FALSE);
+
+ debug_print("RSSyl: starting to update '%s' (%s)\n",
+ ritem->item.name, ritem->url);
+
+ log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATING, ritem->url);
+
+ msg = g_strdup_printf(_("Updating feed '%s'..."), ritem->item.name);
+ STATUSBAR_PUSH(mainwin, msg);
+ g_free(msg);
+
+ GTK_EVENTS_FLUSH();
+
+ /* Prepare context for fetching the feed file */
+ ctx = rssyl_prep_fetchctx_from_item(ritem);
+ g_return_val_if_fail(ctx != NULL, FALSE);
+
+ /* Fetch the feed file */
+ rssyl_fetch_feed(ctx, verbose);
+
+ debug_print("RSSyl: fetch done; success == %s\n",
+ ctx->success ? "TRUE" : "FALSE");
+
+ debug_print("RSSyl: STARTING TO PARSE FEED\n");
+ if( ctx->success && !(ctx->success = rssyl_parse_feed(ritem, ctx->feed)) ) {
+ /* both libcurl and libfeed were happy, but we weren't */
+ debug_print("RSSyl: Error processing feed\n");
+ if( verbose )
+ alertpanel_error(_("Couldn't process feed at\n<b>%s</b>\n\nPlease contact developers, this should not happen."),
+ feed_get_url(ctx->feed));
+
+ log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PROC, ctx->feed->url);
+ }
+
+ debug_print("RSSyl: FEED PARSED\n");
+
+ STATUSBAR_POP(mainwin);
+
+ if( claws_is_exiting() ) {
+ feed_free(ctx->feed);
+ g_free(ctx->error);
+ g_free(ctx);
+ return success;
+ }
+
+ if( ritem->fetch_comments )
+ rssyl_update_comments(ritem);
+
+ /* Prune our deleted items list of items which are no longer in
+ * upstream feed. */
+ rssyl_deleted_expire(ritem, ctx->feed);
+
+ /* Clean up. */
+ success = ctx->success;
+ feed_free(ctx->feed);
+ g_free(ctx->error);
+ g_free(ctx);
+
+ return success;
+}
+
+static gboolean rssyl_update_recursively_func(GNode *node, gpointer data)
+{
+ FolderItem *item;
+ RFolderItem *ritem;
+
+ g_return_val_if_fail(node->data != NULL, FALSE);
+
+ item = FOLDER_ITEM(node->data);
+ ritem = (RFolderItem *)item;
+
+ if( ritem->url != NULL ) {
+ debug_print("RSSyl: Updating feed '%s'\n", item->name);
+ rssyl_update_feed(ritem, FALSE);
+ } else
+ debug_print("RSSyl: Updating in folder '%s'\n", item->name);
+
+ return FALSE;
+}
+
+void rssyl_update_recursively(FolderItem *item)
+{
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ if( item->folder->klass != rssyl_folder_get_class() )
+ return;
+
+ debug_print("Recursively updating '%s'\n", item->name);
+
+ g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ rssyl_update_recursively_func, NULL);
+}
+
+void rssyl_update_all_func(FolderItem *item, gpointer data)
+{
+ /* Only try to refresh our feed folders */
+ if( !IS_RSSYL_FOLDER_ITEM(item) )
+ return;
+
+ if( folder_item_parent(item) == NULL )
+ rssyl_update_recursively(item);
+}
+
+void rssyl_update_all_feeds(void)
+{
+ if (prefs_common.work_offline &&
+ !inc_offline_should_override(TRUE,
+ _("Claws Mail needs network access in order to update your feeds.")) ) {
+ return;
+ }
+
+ folder_func_to_all_folders((FolderItemFunc)rssyl_update_all_func, NULL);
+}
diff --git a/src/plugins/rssyl/rssyl_update_feed.h b/src/plugins/rssyl/rssyl_update_feed.h
new file mode 100644
index 0000000..f72d04a
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_feed.h
@@ -0,0 +1,19 @@
+#ifndef __RSSYL_UPDATE_FEED
+#define __RSSYL_UPDATE_FEED
+
+#include <glib.h>
+
+#include "rssyl.h"
+
+void rssyl_fetch_feed(RFetchCtx *ctx, gboolean verbose);
+
+RFetchCtx *rssyl_prep_fetchctx_from_url(gchar *url);
+RFetchCtx *rssyl_prep_fetchctx_from_item(RFolderItem *ritem);
+
+gboolean rssyl_update_feed(RFolderItem *ritem, gboolean verbose);
+
+void rssyl_update_recursively(FolderItem *item);
+
+void rssyl_update_all_feeds(void);
+
+#endif /* __RSSYL_UPDATE_FEED */
diff --git a/src/plugins/rssyl/rssyl_update_format.c b/src/plugins/rssyl/rssyl_update_format.c
new file mode 100644
index 0000000..8284450
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_format.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2006 Andrej Kacian <andrej at kacian.sk>
+ *
+ * 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
+
+/* Global includes */
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+/* Claws Mail includes */
+#include <alertpanel.h>
+#include <log.h>
+#include <mainwindow.h>
+
+/* Local includes */
+#include "old_feeds.h"
+#include "rssyl.h"
+#include "rssyl_feed.h"
+#include "strutils.h"
+
+struct _RUpdateFormatCtx {
+ FolderItem *o_prev;
+ FolderItem *o_parent;
+ FolderItem *n_prev;
+ FolderItem *n_parent;
+ Folder *n_first;
+ GSList *oldfeeds;
+ GSList *oldroots;
+ gboolean reached_first_new;
+};
+
+typedef struct _RUpdateFormatCtx RUpdateFormatCtx;
+
+extern FolderClass rssyl_class;
+
+static void rssyl_update_format_move_contents(FolderItem *olditem,
+ FolderItem *newitem);
+static gchar *_old_rssyl_item_get_path(Folder *folder, FolderItem *item);
+static void _delete_old_roots_func(gpointer data, gpointer user_data);
+
+static void rssyl_update_format_func(FolderItem *item, gpointer data)
+{
+ RFolderItem *ritem;
+ RUpdateFormatCtx *ctx = (RUpdateFormatCtx *)data;
+ Folder *f = NULL;
+ FolderItem *new_item = NULL;
+ gchar *name;
+ OldRFeed *of;
+
+ if( !IS_RSSYL_FOLDER_ITEM(item) )
+ return;
+
+ /* Do not do anything once we reached first new folder
+ * (which we created earlier in this process) */
+ if( ctx->reached_first_new )
+ return;
+
+ if( item->folder == ctx->n_first ) {
+ ctx->reached_first_new = TRUE;
+ debug_print("RSSyl: (FORMAT) reached first new folder\n");
+ return;
+ }
+
+ debug_print("RSSyl: (FORMAT) item '%s'\n", item->name);
+
+ if( folder_item_parent(item) == NULL ) {
+ /* Root rssyl folder */
+ ctx->oldroots = g_slist_prepend(ctx->oldroots, item);
+
+ /* Create its counterpart */
+ name = rssyl_strreplace(folder_item_get_name(item), " (RSSyl)", "");
+ debug_print("RSSyl: (FORMAT) adding new root folder '%s'\n", name);
+ f = folder_new(rssyl_folder_get_class(), name, NULL);
+ g_free(name);
+ g_return_if_fail(f != NULL);
+ folder_add(f);
+
+ folder_write_list();
+
+ new_item = FOLDER_ITEM(f->node->data);
+
+ /* If user has more than one old rssyl foldertrees, keep the n_first
+ * pointer at the beginning of first one. */
+ if (ctx->n_first == NULL)
+ ctx->n_first = f;
+
+ ctx->n_parent = new_item;
+ } else {
+ /* Non-root folder */
+
+ if (folder_item_parent(item) == ctx->o_prev) {
+ /* We went one step deeper in folder hierarchy, adjust pointers
+ * to parents */
+ ctx->o_parent = ctx->o_prev;
+ ctx->n_parent = ctx->n_prev;
+ } else if (folder_item_parent(item) != ctx->o_parent) {
+ /* We are not in same folder anymore, which can only mean we have
+ * moved up in the hierarchy. Find a correct parent */
+ while (folder_item_parent(item) != ctx->o_parent) {
+ ctx->o_parent = folder_item_parent(ctx->o_parent);
+ ctx->n_parent = folder_item_parent(ctx->n_parent);
+ if (ctx->o_parent == NULL) {
+ /* This shouldn't happen, unless we are magically moved to a
+ * completely different folder structure */
+ debug_print("RSSyl: MISHAP WHILE UPGRADING STORAGE FORMAT: couldn't find folder parent\n");
+ alertpanel_error(_("Internal problem while upgrading storage format. This should not happen. Please report this, with debug output attached.\n"));
+ return;
+ }
+ }
+ } else {
+ /* We have remained in the same subfolder, nothing to do here */
+ }
+
+ debug_print("RSSyl: (FORMAT) adding folder '%s'\n", item->name);
+ new_item = folder_create_folder(ctx->n_parent, item->name);
+
+ if (new_item == NULL) {
+ debug_print("RSSyl: (FORMAT) couldn't add folder '%s', skipping it\n",
+ item->name);
+ return;
+ }
+
+ of = rssyl_old_feed_get_by_name(ctx->oldfeeds, item->name);
+ if (of != NULL && of->url != NULL) {
+ /* Folder with an actual subscribed feed */
+ debug_print("RSSyl: (FORMAT) making '%s' a feed with URL '%s'\n",
+ item->name, of->url);
+
+ ritem = (RFolderItem *)new_item;
+ ritem->url = g_strdup(of->url);
+
+ rssyl_feed_start_refresh_timeout(ritem);
+
+ /* TODO: copy feed preferences from old structure */
+ ritem->official_title = g_strdup(of->official_name);
+ ritem->default_refresh_interval =
+ (of->default_refresh_interval != 0 ? TRUE : FALSE);
+ ritem->refresh_interval = of->refresh_interval;
+ ritem->keep_old = (of->expired_num > -1 ? TRUE : FALSE);
+ ritem->fetch_comments =
+ (of->fetch_comments != 0 ? TRUE : FALSE);
+ ritem->fetch_comments_max_age = of->fetch_comments_for;
+ ritem->silent_update = of->silent_update;
+ }
+
+ rssyl_update_format_move_contents(item, new_item);
+
+ /* Store folderlist with the new folder */
+ folder_item_scan(new_item);
+ folder_write_list();
+ }
+
+ ctx->o_prev = item;
+ ctx->n_prev = new_item;
+}
+
+
+void rssyl_update_format()
+{
+ RUpdateFormatCtx *ctx = NULL;
+ GSList *oldfeeds;
+ gchar *old_feeds_xml = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ RSSYL_DIR, G_DIR_SEPARATOR_S, "feeds.xml", NULL);
+
+ if (!g_file_test(old_feeds_xml,
+ G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ g_free(old_feeds_xml);
+ return;
+ }
+
+ debug_print("RSSyl: Old format found, updating.\n");
+
+ oldfeeds = rssyl_old_feed_metadata_parse(old_feeds_xml);
+
+ /* We find all rssyl root folders and perform magic on each */
+ ctx = g_new0(RUpdateFormatCtx, 1);
+ ctx->o_prev = NULL;
+ ctx->o_parent = NULL;
+ ctx->n_prev = NULL;
+ ctx->n_parent = NULL;
+ ctx->n_first = NULL;
+ ctx->oldfeeds = oldfeeds;
+ ctx->oldroots = NULL;
+ ctx->reached_first_new = FALSE;
+
+ folder_item_update_freeze();
+
+ /* Go through all RSSyl folders, making new copies */
+ folder_func_to_all_folders((FolderItemFunc)rssyl_update_format_func, ctx);
+
+ g_slist_foreach(ctx->oldroots, _delete_old_roots_func, NULL);
+ g_slist_free(ctx->oldroots);
+
+ folder_item_update_thaw();
+
+ g_free(ctx);
+
+ g_remove(old_feeds_xml);
+ g_free(old_feeds_xml);
+}
+
+static void _delete_old_roots_func(gpointer data, gpointer user_data)
+{
+ FolderItem *item = (FolderItem *)data;
+
+ folder_destroy(item->folder);
+}
+
+/* Copy each item in a feed to the new directory */
+static void rssyl_update_format_move_contents(FolderItem *olditem,
+ FolderItem *newitem)
+{
+ gchar *oldpath, *newpath, *fname, *fpath, *nfpath;
+ GDir *d = NULL;
+ GError *error = NULL;
+
+ oldpath = _old_rssyl_item_get_path(NULL, olditem);
+ newpath = folder_item_get_path(newitem);
+
+ if ((d = g_dir_open(oldpath, 0, &error)) == NULL) {
+ debug_print("RSSyl: (FORMAT) couldn't open dir '%s': %s\n", oldpath,
+ error->message);
+ g_error_free(error);
+ return;
+ }
+
+ debug_print("RSSyl: (FORMAT) moving contents of '%s' to '%s'\n",
+ oldpath, newpath);
+
+ while ((fname = (gchar *)g_dir_read_name(d)) != NULL) {
+ fpath = g_strconcat(oldpath, G_DIR_SEPARATOR_S, fname, NULL);
+ if (to_number(fname) > 0 && g_file_test(fpath, G_FILE_TEST_IS_REGULAR)) {
+ nfpath = g_strconcat(newpath, G_DIR_SEPARATOR_S, fname, NULL);
+ move_file(fpath, nfpath, FALSE);
+ g_free(nfpath);
+ }
+ g_remove(fpath);
+ g_free(fpath);
+ }
+
+ g_dir_close(d);
+ g_rmdir(oldpath);
+
+ g_free(oldpath);
+ g_free(newpath);
+}
+
+static gchar *_old_rssyl_item_get_path(Folder *folder, FolderItem *item)
+{
+ gchar *result, *tmp;
+
+ if (folder_item_parent(item) == NULL)
+ return g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR, NULL);
+
+ tmp = rssyl_strreplace(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;
+}
diff --git a/src/plugins/rssyl/rssyl_update_format.h b/src/plugins/rssyl/rssyl_update_format.h
new file mode 100644
index 0000000..7e3a858
--- /dev/null
+++ b/src/plugins/rssyl/rssyl_update_format.h
@@ -0,0 +1,6 @@
+#ifndef __RSSYL_UPDATE_FORMAT
+#define __RSSYL_UPDATE_FORMAT
+
+void rssyl_update_format();
+
+#endif /* __RSSYL_UPDATE_FORMAT */
diff --git a/src/plugins/rssyl/strreplace.c b/src/plugins/rssyl/strreplace.c
deleted file mode 100644
index 7d4322c..0000000
--- a/src/plugins/rssyl/strreplace.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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;
-}
diff --git a/src/plugins/rssyl/strreplace.h b/src/plugins/rssyl/strreplace.h
deleted file mode 100644
index 37c29dc..0000000
--- a/src/plugins/rssyl/strreplace.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#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 */
diff --git a/src/plugins/rssyl/strutils.c b/src/plugins/rssyl/strutils.c
new file mode 100644
index 0000000..3d9f1c5
--- /dev/null
+++ b/src/plugins/rssyl/strutils.c
@@ -0,0 +1,289 @@
+/*
+ * Claws-Mail-- 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
+
+/* Global includes */
+#include <glib.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+/* Claws Mail includes */
+#include <common/utils.h>
+
+/* Local includes */
+/* (shouldn't be any) */
+
+gchar *rssyl_strreplace(gchar *source, gchar *pattern,
+ gchar *replacement)
+{
+ gchar *new, *w_new = NULL, *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 source;
+ }
+
+ if( !g_utf8_validate(source, -1, NULL) ) {
+ debug_print("RSSyl: source is not an UTF-8 encoded text\n");
+ return source;
+ }
+
+ if( !g_utf8_validate(pattern, -1, NULL) ) {
+ debug_print("RSSyl: pattern is not an UTF-8 encoded text\n");
+ return source;
+ }
+
+ 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;
+}
+
+typedef struct _RSSyl_HTMLSymbol RSSyl_HTMLSymbol;
+struct _RSSyl_HTMLSymbol
+{
+ gchar *const key;
+ gchar *const val;
+};
+
+static RSSyl_HTMLSymbol symbol_list[] = {
+ { "<", "<" },
+ { ">", ">" },
+ { "&", "&" },
+ { """, "\"" },
+ { "‘", "'" },
+ { "’", "'" },
+ { "“", "\"" },
+ { "”", "\"" },
+ { " ", " " },
+ { "™", "(TM)" },
+ { "", "(TM)" },
+ { "'", "'" },
+ { "…", "..." },
+ { "…", "..." },
+ { "—", "-" },
+ { NULL, NULL }
+};
+
+static RSSyl_HTMLSymbol tag_list[] = {
+ { "<cite>", "\"" },
+ { "</cite>", "\"" },
+ { "<i>", "" },
+ { "</i>", "" },
+ { "<em>", "" },
+ { "</em>", "" },
+ { "<b>", "" },
+ { "</b>", "" },
+ { "<nobr>", "" },
+ { "</nobr>", "" },
+ { "<wbr>", "" },
+ { NULL, NULL }
+};
+
+gchar *rssyl_replace_html_stuff(gchar *text,
+ gboolean symbols, gboolean tags)
+{
+ gchar *tmp = NULL, *wtext = NULL;
+ gint i;
+
+ g_return_val_if_fail(text != NULL, NULL);
+
+ wtext = g_strdup(text);
+
+ /* Ugly, needlessly traverses the string again and again. Probably
+ * could use a rewrite. */
+ if( symbols ) {
+ for( i = 0; symbol_list[i].key != NULL; i++ ) {
+ if( g_strstr_len(text, strlen(text), symbol_list[i].key) ) {
+ tmp = rssyl_strreplace(wtext, symbol_list[i].key, symbol_list[i].val);
+ wtext = g_strdup(tmp);
+ g_free(tmp);
+ }
+ }
+ }
+
+ if( tags ) {
+ for( i = 0; tag_list[i].key != NULL; i++ ) {
+ if( g_strstr_len(text, strlen(text), symbol_list[i].key) ) {
+ tmp = rssyl_strreplace(wtext, tag_list[i].key, tag_list[i].val);
+ wtext = g_strdup(tmp);
+ g_free(tmp);
+ }
+ }
+ }
+
+ return wtext;
+}
+
+static gchar *rssyl_sanitize_string(gchar *str, gboolean strip_nl)
+{
+ gchar *new = NULL, *c = str, *n = NULL;
+
+ if( str == NULL )
+ return NULL;
+
+ n = new = malloc(strlen(str) + 1);
+ memset(new, '\0', strlen(str) + 1);
+
+ while( *c != '\0' ) {
+ if( !isspace(*c) || *c == ' ' || (!strip_nl && *c == '\n') ) {
+ *n = *c;
+ n++;
+ }
+ c++;
+ }
+
+ return new;
+}
+
+/* rssyl_format_string()
+ * - return value needs to be freed
+ */
+gchar *rssyl_format_string(gchar *str, gboolean replace_html,
+ gboolean strip_nl)
+{
+ gchar *res = NULL, *tmp = NULL;
+
+ g_return_val_if_fail(str != NULL, NULL);
+
+ if (replace_html)
+ tmp = rssyl_replace_html_stuff(str, TRUE, TRUE);
+ else
+ tmp = g_strdup(str);
+
+ res = rssyl_sanitize_string(tmp, strip_nl);
+ g_free(tmp);
+
+ g_strstrip(res);
+
+ return res;
+}
+
+/* this functions splits a string into an array of string, by
+ * returning an array of pointers to positions of the delimiter
+ * in the original string and replacing this delimiter with a
+ * NULL. It does not duplicate memory, hence you should only
+ * free the array and not its elements, and you should not
+ * free the original string before you're done with the array.
+ * maybe could be part of the core (utils.c).
+ */
+gchar **strsplit_no_copy(gchar *str, char delimiter)
+{
+ gchar **array = g_new(gchar *, 1);
+ int i = 0;
+ gchar *cur = str, *next;
+
+ array[i] = cur;
+ i++;
+ while ((next = strchr(cur, delimiter)) != NULL) {
+ *(next) = '\0';
+ array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
+ array[i] = next + 1;
+ cur = next + 1;
+ i++;
+ }
+ array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
+ array[i] = NULL;
+ return array;
+}
+
+/* This is a very dumb function - it just strips <, > and everything between
+ * them. */
+void strip_html(gchar *str)
+{
+ gchar *p = str;
+ gboolean intag = FALSE;
+
+ while (*p) {
+ if (*p == '<')
+ intag = TRUE;
+ else if (*p == '>')
+ intag = FALSE;
+
+ if (*p == '<' || *p == '>' || intag)
+ memmove(p, p + 1, strlen(p));
+ else
+ p++;
+ }
+}
+
+gchar *my_normalize_url(const gchar *url)
+{
+ gchar *myurl = NULL;
+
+ if (!strncmp(url, "feed://", 7))
+ myurl = g_strdup(url+7);
+ else if (!strncmp(url, "feed:", 5))
+ myurl = g_strdup(url+5);
+ else
+ myurl = g_strdup(url);
+
+ return myurl;
+}
diff --git a/src/plugins/rssyl/strutils.h b/src/plugins/rssyl/strutils.h
new file mode 100644
index 0000000..2976e6f
--- /dev/null
+++ b/src/plugins/rssyl/strutils.h
@@ -0,0 +1,19 @@
+#ifndef __STRUTILS_H
+#define __STRUTILS_H
+
+gchar *rssyl_strreplace(gchar *source, gchar *pattern,
+ gchar *replacement);
+
+gchar *rssyl_replace_html_stuff(gchar *text,
+ gboolean symbols, gboolean tags);
+
+gchar *rssyl_format_string(gchar *str, gboolean replace_html,
+ gboolean strip_nl);
+
+gchar **strsplit_no_copy(gchar *str, char delimiter);
+
+void strip_html(gchar *str);
+
+gchar *my_normalize_url(const gchar *url);
+
+#endif /* __STRUTILS_H */
-----------------------------------------------------------------------
hooks/post-receive
--
Claws Mail
More information about the Commits
mailing list