2013-02-16 [colin] 3.9.0cvs70
authorColin Leroy <colin@colino.net>
Sat, 16 Feb 2013 10:35:15 +0000 (10:35 +0000)
committerColin Leroy <colin@colino.net>
Sat, 16 Feb 2013 10:35:15 +0000 (10:35 +0000)
* src/main.c
* src/prefs_common.c
* src/prefs_common.h
* src/common/defs.h
* src/common/w32_account.c
Add an hidden preference to set the Windows theme
* src/plugins/Makefile.am
* src/plugins/rssyl/Makefile.am
* src/plugins/rssyl/claws.def
* src/plugins/rssyl/date.c
* src/plugins/rssyl/date.h
* src/plugins/rssyl/feed.c
* src/plugins/rssyl/feed.h
* src/plugins/rssyl/feedprops.c
* src/plugins/rssyl/feedprops.h
* src/plugins/rssyl/opml.c
* src/plugins/rssyl/opml.h
* src/plugins/rssyl/parsers.c
* src/plugins/rssyl/parsers.h
* src/plugins/rssyl/placeholder.txt
* src/plugins/rssyl/plugin.c
* src/plugins/rssyl/plugin.def
* src/plugins/rssyl/rssyl.c
* src/plugins/rssyl/rssyl.h
* src/plugins/rssyl/rssyl_cb_gtk.c
* src/plugins/rssyl/rssyl_cb_gtk.h
* src/plugins/rssyl/rssyl_cb_menu.c
* src/plugins/rssyl/rssyl_cb_menu.h
* src/plugins/rssyl/rssyl_gtk.c
* src/plugins/rssyl/rssyl_gtk.h
* src/plugins/rssyl/rssyl_prefs.c
* src/plugins/rssyl/rssyl_prefs.h
* src/plugins/rssyl/strreplace.c
* src/plugins/rssyl/strreplace.h
* src/plugins/rssyl/version.rc
Add RSSyl
* src/plugins/vcalendar/Makefile.am
* src/plugins/vcalendar/Makefile.in
* src/plugins/vcalendar/plugin.c
* src/plugins/vcalendar/libical/libical/icalversion.h
Fix Curl flags :)

41 files changed:
ChangeLog
PATCHSETS
configure.ac
src/common/defs.h
src/common/w32_account.c
src/main.c
src/plugins/Makefile.am
src/plugins/rssyl/Makefile.am [new file with mode: 0644]
src/plugins/rssyl/claws.def [new file with mode: 0644]
src/plugins/rssyl/date.c [new file with mode: 0644]
src/plugins/rssyl/date.h [new file with mode: 0644]
src/plugins/rssyl/feed.c [new file with mode: 0644]
src/plugins/rssyl/feed.h [new file with mode: 0644]
src/plugins/rssyl/feedprops.c [new file with mode: 0644]
src/plugins/rssyl/feedprops.h [new file with mode: 0644]
src/plugins/rssyl/opml.c [new file with mode: 0644]
src/plugins/rssyl/opml.h [new file with mode: 0644]
src/plugins/rssyl/parsers.c [new file with mode: 0644]
src/plugins/rssyl/parsers.h [new file with mode: 0644]
src/plugins/rssyl/placeholder.txt [deleted file]
src/plugins/rssyl/plugin.c [new file with mode: 0644]
src/plugins/rssyl/plugin.def [new file with mode: 0644]
src/plugins/rssyl/rssyl.c [new file with mode: 0644]
src/plugins/rssyl/rssyl.h [new file with mode: 0644]
src/plugins/rssyl/rssyl_cb_gtk.c [new file with mode: 0644]
src/plugins/rssyl/rssyl_cb_gtk.h [new file with mode: 0644]
src/plugins/rssyl/rssyl_cb_menu.c [new file with mode: 0644]
src/plugins/rssyl/rssyl_cb_menu.h [new file with mode: 0644]
src/plugins/rssyl/rssyl_gtk.c [new file with mode: 0644]
src/plugins/rssyl/rssyl_gtk.h [new file with mode: 0644]
src/plugins/rssyl/rssyl_prefs.c [new file with mode: 0644]
src/plugins/rssyl/rssyl_prefs.h [new file with mode: 0644]
src/plugins/rssyl/strreplace.c [new file with mode: 0644]
src/plugins/rssyl/strreplace.h [new file with mode: 0644]
src/plugins/rssyl/version.rc [new file with mode: 0644]
src/plugins/vcalendar/Makefile.am
src/plugins/vcalendar/Makefile.in
src/plugins/vcalendar/libical/libical/icalversion.h
src/plugins/vcalendar/plugin.c
src/prefs_common.c
src/prefs_common.h

index 630f237..0273b53 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2013-02-16 [colin]     3.9.0cvs70
+
+       * src/main.c
+       * src/prefs_common.c
+       * src/prefs_common.h
+       * src/common/defs.h
+       * src/common/w32_account.c
+               Add an hidden preference to set the Windows theme
+       * src/plugins/Makefile.am
+       * src/plugins/rssyl/Makefile.am
+       * src/plugins/rssyl/claws.def
+       * src/plugins/rssyl/date.c
+       * src/plugins/rssyl/date.h
+       * src/plugins/rssyl/feed.c
+       * src/plugins/rssyl/feed.h
+       * src/plugins/rssyl/feedprops.c
+       * src/plugins/rssyl/feedprops.h
+       * src/plugins/rssyl/opml.c
+       * src/plugins/rssyl/opml.h
+       * src/plugins/rssyl/parsers.c
+       * src/plugins/rssyl/parsers.h
+       * src/plugins/rssyl/placeholder.txt
+       * src/plugins/rssyl/plugin.c
+       * src/plugins/rssyl/plugin.def
+       * src/plugins/rssyl/rssyl.c
+       * src/plugins/rssyl/rssyl.h
+       * src/plugins/rssyl/rssyl_cb_gtk.c
+       * src/plugins/rssyl/rssyl_cb_gtk.h
+       * src/plugins/rssyl/rssyl_cb_menu.c
+       * src/plugins/rssyl/rssyl_cb_menu.h
+       * src/plugins/rssyl/rssyl_gtk.c
+       * src/plugins/rssyl/rssyl_gtk.h
+       * src/plugins/rssyl/rssyl_prefs.c
+       * src/plugins/rssyl/rssyl_prefs.h
+       * src/plugins/rssyl/strreplace.c
+       * src/plugins/rssyl/strreplace.h
+       * src/plugins/rssyl/version.rc
+               Add RSSyl
+       * src/plugins/vcalendar/Makefile.am
+       * src/plugins/vcalendar/Makefile.in
+       * src/plugins/vcalendar/plugin.c
+       * src/plugins/vcalendar/libical/libical/icalversion.h
+               Fix Curl flags :)
+
 2013-02-14 [paul]      3.9.0cvs69
 
        * po/id_ID.po
index 84079eb..ecfa50a 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.654.2.4643 -r 1.654.2.4644 configure.ac;  ) > 3.9.0cvs67.patchset
 ( cvs diff -u -r 1.53.2.40 -r 1.53.2.41 po/POTFILES.in;  cvs diff -u -r 1.21.2.10 -r 1.21.2.11 po/bg.po;  cvs diff -u -r 1.1.2.20 -r 1.1.2.21 po/ca.po;  cvs diff -u -r 1.9.2.27 -r 1.9.2.28 po/cs.po;  cvs diff -u -r 1.58.2.53 -r 1.58.2.54 po/de.po;  cvs diff -u -r 1.12.2.19 -r 1.12.2.20 po/en_GB.po;  cvs diff -u -r 1.60.2.67 -r 1.60.2.68 po/es.po;  cvs diff -u -r 1.1.2.30 -r 1.1.2.31 po/fi.po;  cvs diff -u -r 1.42.2.60 -r 1.42.2.61 po/fr.po;  cvs diff -u -r 1.5.2.24 -r 1.5.2.25 po/hu.po;  cvs diff -u -r 1.1.2.3 -r 1.1.2.4 po/id_ID.po;  cvs diff -u -r 1.34.2.31 -r 1.34.2.32 po/it.po;  cvs diff -u -r 1.16.2.13 -r 1.16.2.14 po/ja.po;  cvs diff -u -r 1.1.2.6 -r 1.1.2.7 po/lt.po;  diff -u /dev/null po/merge-po-from-plugin.sh;  cvs diff -u -r 1.28.2.16 -r 1.28.2.17 po/nl.po;  cvs diff -u -r 1.10.2.19 -r 1.10.2.20 po/pl.po;  cvs diff -u -r 1.50.2.46 -r 1.50.2.47 po/pt_BR.po;  cvs diff -u -r 1.1.2.5 -r 1.1.2.6 po/pt_PT.po;  cvs diff -u -r 1.17.2.23 -r 1.17.2.24 po/ru.po;  cvs diff -u -r 1.2.2.38 -r 1.2.2.39 po/sk.po;  cvs diff -u -r 1.17.2.29 -r 1.17.2.30 po/sr.po;  cvs diff -u -r 1.4.2.14 -r 1.4.2.15 po/sv.po;  cvs diff -u -r 1.1.2.7 -r 1.1.2.8 po/uk.po;  cvs diff -u -r 1.5.2.32 -r 1.5.2.33 po/zh_CN.po;  cvs diff -u -r 1.1.2.6 -r 1.1.2.7 po/zh_TW.po;  ) > 3.9.0cvs68.patchset
 ( cvs diff -u -r 1.1.2.4 -r 1.1.2.5 po/id_ID.po;  ) > 3.9.0cvs69.patchset
+( cvs diff -u -r 1.115.2.267 -r 1.115.2.268 src/main.c;  cvs diff -u -r 1.204.2.214 -r 1.204.2.215 src/prefs_common.c;  cvs diff -u -r 1.103.2.143 -r 1.103.2.144 src/prefs_common.h;  cvs diff -u -r 1.9.2.62 -r 1.9.2.63 src/common/defs.h;  cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/common/w32_account.c;  cvs diff -u -r 1.8.2.10 -r 1.8.2.11 src/plugins/Makefile.am;  diff -u /dev/null src/plugins/rssyl/Makefile.am;  diff -u /dev/null src/plugins/rssyl/claws.def;  diff -u /dev/null src/plugins/rssyl/date.c;  diff -u /dev/null src/plugins/rssyl/date.h;  diff -u /dev/null src/plugins/rssyl/feed.c;  diff -u /dev/null src/plugins/rssyl/feed.h;  diff -u /dev/null src/plugins/rssyl/feedprops.c;  diff -u /dev/null src/plugins/rssyl/feedprops.h;  diff -u /dev/null src/plugins/rssyl/opml.c;  diff -u /dev/null src/plugins/rssyl/opml.h;  diff -u /dev/null src/plugins/rssyl/parsers.c;  diff -u /dev/null src/plugins/rssyl/parsers.h;  cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/rssyl/placeholder.txt;  diff -u /dev/null src/plugins/rssyl/plugin.c;  diff -u /dev/null src/plugins/rssyl/plugin.def;  diff -u /dev/null src/plugins/rssyl/rssyl.c;  diff -u /dev/null src/plugins/rssyl/rssyl.h;  diff -u /dev/null src/plugins/rssyl/rssyl_cb_gtk.c;  diff -u /dev/null src/plugins/rssyl/rssyl_cb_gtk.h;  diff -u /dev/null src/plugins/rssyl/rssyl_cb_menu.c;  diff -u /dev/null src/plugins/rssyl/rssyl_cb_menu.h;  diff -u /dev/null src/plugins/rssyl/rssyl_gtk.c;  diff -u /dev/null src/plugins/rssyl/rssyl_gtk.h;  diff -u /dev/null src/plugins/rssyl/rssyl_prefs.c;  diff -u /dev/null src/plugins/rssyl/rssyl_prefs.h;  diff -u /dev/null src/plugins/rssyl/strreplace.c;  diff -u /dev/null src/plugins/rssyl/strreplace.h;  diff -u /dev/null src/plugins/rssyl/version.rc;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/vcalendar/Makefile.am;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/vcalendar/Makefile.in;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/vcalendar/plugin.c;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/vcalendar/libical/libical/icalversion.h;  ) > 3.9.0cvs70.patchset
index d20caaa..2dfce40 100644 (file)
@@ -12,7 +12,7 @@ MINOR_VERSION=9
 MICRO_VERSION=0
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=69
+EXTRA_VERSION=70
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
@@ -1028,7 +1028,7 @@ if test x"$ac_cv_enable_fancy_plugin" = xyes; then
 fi
 AM_CONDITIONAL(BUILD_FANCY_PLUGIN, test x"$ac_cv_enable_fancy_plugin" = xyes)
 
-dnl --- Fancy ---
+dnl --- vCalendar ---
 AC_MSG_CHECKING([whether to build vCalendar plugin])
 AC_ARG_ENABLE(vcalendar-plugin,
        [  --disable-vcalendar-plugin     do not build vcalendar plugin for ical rendering],
@@ -1045,6 +1045,21 @@ if test x"$ac_cv_enable_vcalendar_plugin" = xyes; then
 fi
 AM_CONDITIONAL(BUILD_VCALENDAR_PLUGIN, test x"$ac_cv_enable_vcalendar_plugin" = xyes)
 
+dnl --- RSSyl ---
+AC_MSG_CHECKING([whether to build RSSyl plugin])
+AC_ARG_ENABLE(rssyl-plugin,
+       [  --disable-rssyl-plugin     do not build rssyl plugin for RSS and Atom feeds],
+       [ac_cv_enable_rssyl_plugin=$enableval], [ac_cv_enable_rssyl_plugin=yes])
+if test x"$ac_cv_enable_rssyl_plugin" = xyes; then
+       AC_MSG_RESULT(yes)
+       PKG_CHECK_MODULES(LIBXML, "libxml-2.0",,
+               [AC_MSG_ERROR(libxml library is missing)])
+       PLUGINS="rssyl $PLUGINS"
+       AC_SUBST(LIBXML_CFLAGS)
+       AC_SUBST(LIBXML_LIBS)
+fi
+AM_CONDITIONAL(BUILD_RSSYL_PLUGIN, test x"$ac_cv_enable_rssyl_plugin" = xyes)
+
 dnl --- Demo ---
 AC_ARG_ENABLE(demo-plugin,
        [  --enable-demo-plugin              build demo plugin],
@@ -1141,6 +1156,7 @@ src/plugins/trayicon/Makefile
 src/plugins/pgpcore/Makefile
 src/plugins/pgpmime/Makefile
 src/plugins/pgpinline/Makefile
+src/plugins/rssyl/Makefile
 src/plugins/smime/Makefile
 src/plugins/vcalendar/Makefile
 src/plugins/vcalendar/libical/Makefile
index dfbf8e2..cd27480 100644 (file)
 #  define ACTIONS_RC           "actionswinrc"
 #  define COMMAND_HISTORY      "command_history_win"
 #  define DEFAULT_SIGNATURE    "signature.txt"
+#  define DEFAULT_W32_GTK_THEME "MS-Windows"
 #else
 #  define ACTIONS_RC           "actionsrc"
 #  define COMMAND_HISTORY      "command_history"
index 6e9fca8..37faa97 100644 (file)
 
 #include <stdlib.h>
 #include <string.h>
+#include <windows.h>
+#include <sddl.h> // for ConvertSidToStringSid()
 
 #include "w32lib.h"
+#include "utils.h"
 
 #ifndef DIM
 #define DIM(v)  (sizeof(v)/sizeof((v)[0]))
@@ -147,3 +150,62 @@ w32_is_administrator (void)
   return is_admin;
 }
 
+/* Vomit bags are available under your seats */
+int w32_get_uid_equiv(void)
+{
+       HANDLE Thandle;
+       DWORD size;
+       PSID sid;
+       char *sidStr = NULL;
+       char *last_sep = NULL;
+       static int siduid = -1;
+       static BOOL (WINAPI *GetTokenInformation)(
+         HANDLE TokenHandle,
+         TOKEN_INFORMATION_CLASS TokenInformationClass,
+         LPVOID TokenInformation,
+         DWORD TokenInformationLength,
+         PDWORD ReturnLength
+       );
+       static  BOOL (WINAPI *ConvertSidToStringSid)(
+         PSID Sid,
+         LPTSTR *StringSid
+       );
+       static int TokenLogonSid = 28 /* according to Google. */;
+
+       if (siduid == -1) {
+               void *handle = LoadLibrary ("advapi32.dll");
+               if (handle) {
+                       GetTokenInformation = GetProcAddress (handle, "GetTokenInformation");
+                       if (!GetTokenInformation) {
+                               FreeLibrary (handle);
+                               debug_print("can't resolve GetTokenInformation\n");
+                               return -1;
+                       }
+               }
+
+               if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &Thandle) == 0
+                   && OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Thandle) == 0) {
+                       debug_print("OpenProcessToken error %d\n", GetLastError());
+                       return -1;
+               }
+
+               if (GetTokenInformation(Thandle, TokenLogonSid, &sid, sizeof(sid), &size) == 0) {
+                       debug_print("GetTokenInformation error %d\n", GetLastError());
+                       return -1;
+               }
+               if (ConvertSidToStringSid((PSID)sid, &sidStr) == 0) {
+                       debug_print("ConvertSidToStringSid error %d\n", GetLastError());
+                       return -1;
+               }
+
+               debug_print("SID %s\n", sidStr);
+               last_sep = strrchr(sidStr, '-');
+               if (last_sep != NULL) {
+                       last_sep++;
+                       siduid = strtol(last_sep, NULL, 10);
+                       debug_print("Using UID equivalent %d\n", siduid);
+               }
+               LocalFree(sidStr);
+       }
+       return siduid;
+}
index 79d0688..16a3378 100644 (file)
@@ -1355,6 +1355,15 @@ int main(int argc, char *argv[])
        prefs_spelling_init();
 #endif
 
+#ifdef G_OS_WIN32
+       if(strcmp(prefs_common.gtk_theme, DEFAULT_W32_GTK_THEME))
+               gtk_settings_set_string_property(gtk_settings_get_default(),
+                       "gtk-theme-name",
+                       prefs_common.gtk_theme,
+                       "XProperty");
+#endif
+
+
        sock_set_io_timeout(prefs_common.io_timeout_secs);
        prefs_actions_read_config();
        prefs_display_header_read_config();
index 56efe51..a72d174 100644 (file)
@@ -22,6 +22,10 @@ if BUILD_VCALENDAR_PLUGIN
 vcalendar_dir = vcalendar
 endif
 
+if BUILD_RSSYL_PLUGIN
+rssyl_dir = rssyl
+endif
+
 if BUILD_TRAYICON_PLUGIN
 trayicon_dir = trayicon
 endif
@@ -48,5 +52,6 @@ SUBDIRS = $(demo_dir) \
        $(pgpcore_dir) \
        $(pgpmime_dir) \
        $(pgpinline_dir) \
+       $(rssyl_dir) \
        $(smime_dir) \
        $(vcalendar_dir)
diff --git a/src/plugins/rssyl/Makefile.am b/src/plugins/rssyl/Makefile.am
new file mode 100644 (file)
index 0000000..81f580e
--- /dev/null
@@ -0,0 +1,86 @@
+EXTRA_DIST = claws.def plugin.def version.rc
+
+if OS_WIN32
+
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RC) \
+     `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
+     sed -e 's/-I/--include-dir /g;s/-D/--define /g'`
+
+%.lo : %.rc
+       $(LTRCCOMPILE) -i $< -o $@
+
+plugin_res = version.lo
+plugin_res_ldflag = -Wl,.libs/version.o
+
+export_symbols = -export-symbols $(srcdir)/plugin.def
+
+plugin_deps = libclaws.a $(plugin_res) plugin.def
+
+libclaws.a: claws.def
+       $(DLLTOOL) --output-lib $@ --def $<
+
+plugin_ldadd = -L. -lclaws
+
+else
+plugin_res =
+plugin_res_ldflag =
+export_symbols =
+plugin_deps =
+plugin_ldadd =
+endif
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+else
+no_undefined =
+endif
+
+if CYGWIN
+cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
+else
+cygwin_export_lib = 
+endif
+
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = rssyl.la
+
+INCLUDES= \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/common \
+       -I$(top_builddir)/src/common \
+       -I$(top_srcdir)/src/gtk
+
+rssyl_la_LDFLAGS = \
+       $(plugin_res_ldflag) $(no_undefined) $(export_symbols) \
+       -avoid-version -module \
+       $(GTK_LIBS)
+
+rssyl_la_DEPENDENCIES = $(plugin_deps)
+
+rssyl_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
+       $(GTK_LIBS) $(LIBXML_LIBS) $(CURL_LIBS)
+
+AM_CPPFLAGS = \
+       -Wall \
+       $(CLAWS_MAIL_CFLAGS) \
+       $(GLIB_CFLAGS) \
+       $(GTK_CFLAGS) \
+       $(LIBXML_CFLAGS) \
+       $(CURL_CFLAGS) \
+       -DLOCALEDIR=\""$(localedir)"\" 
+
+rssyl_la_SOURCES = \
+       date.c date.h \
+       feed.c feed.h \
+       feedprops.c feedprops.h \
+       opml.c opml.h \
+       parsers.c parsers.h \
+       plugin.c \
+       rssyl.c rssyl.h \
+       rssyl_cb_gtk.c rssyl_cb_gtk.h \
+       rssyl_cb_menu.c rssyl_cb_menu.h \
+       rssyl_gtk.c rssyl_gtk.h \
+       rssyl_prefs.c rssyl_prefs.h \
+       strreplace.c strreplace.h
+
diff --git a/src/plugins/rssyl/claws.def b/src/plugins/rssyl/claws.def
new file mode 100644 (file)
index 0000000..0c2a033
--- /dev/null
@@ -0,0 +1,99 @@
+LIBRARY CLAWS-MAIL.EXE
+EXPORTS
+get_locale_dir
+check_plugin_version
+alertpanel_error
+alertpanel_full
+change_dir
+claws_do_idle
+claws_is_exiting
+claws_is_starting
+claws_unlink
+cm_menu_set_sensitive_full
+conv_encode_header_full
+conv_unmime_header
+copy_file
+debug_print_real
+debug_srcname
+dirent_is_regular_file
+file_exist
+filesel_select_file_open_with_filter
+file_strip_crs
+folder_add
+folder_create_folder
+folder_destroy
+folder_find_child_item_by_name
+folder_find_from_path
+folder_func_to_all_folders
+folder_get_class_from_string
+folder_get_xml
+folder_init
+folder_item_add_msg
+folder_item_append
+folder_item_get_identifier
+folder_item_get_msginfo
+folder_item_get_path
+folder_item_new
+folder_item_parent
+folder_item_prefs_save_config_recursive
+folder_item_remove
+folder_item_rename
+folder_item_scan
+folder_item_update_freeze
+folder_item_update_thaw
+folder_local_folder_destroy
+folder_new
+folder_register_class
+folder_scan_tree
+folder_set_xml
+folder_unregister_class
+folderview_get_selected_item
+folderview_register_popup
+folderview_set
+folderview_unregister_popup
+folderview_unselect
+folder_write_list
+get_rc_dir
+get_tmp_file
+gtk_cmctree_get_type
+gtk_cmctree_is_ancestor
+gtk_cmctree_node_get_row_data
+gtkut_widget_draw_now
+gtkut_window_new
+gtkut_widget_set_can_default
+inc_offline_should_override
+input_dialog
+is_dir_exist
+is_file_entry_exist
+log_error
+log_print
+log_warning
+main_window_cursor_normal
+main_window_cursor_wait
+mainwindow_get_mainwindow
+make_dir
+make_dir_hier
+mkstemp
+prefs_common_get_prefs
+prefs_file_close
+prefs_file_close_revert
+prefs_filtering_delete_path
+prefs_gtk_register_page
+prefs_gtk_unregister_page
+prefs_read_config
+prefs_set_block_label
+prefs_set_default
+prefs_toolbar_register_plugin_item
+prefs_toolbar_unregister_plugin_item
+prefs_write_open
+prefs_write_param
+procheader_date_parse
+procheader_parse_file
+procmsg_get_message_file
+procmsg_msginfo_unset_flags
+strtailchomp
+subst_for_shellsafe_filename
+summary_clear_all
+summary_show
+to_number
+trim_string
diff --git a/src/plugins/rssyl/date.c b/src/plugins/rssyl/date.c
new file mode 100644 (file)
index 0000000..4145f52
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * @file common.c common routines for Liferea
+ * 
+ * Copyright (C) 2003-2005  Lars Lindner <lars.lindner@gmx.net>
+ * Copyright (C) 2004,2005  Nathan J. Conrad <t98502@users.sourceforge.net>
+ * Copyright (C) 2004       Karl Soderstrom <ks@xanadunet.net>
+ *
+ * parts of the RFC822 timezone decoding were taken from the gmime 
+ * source written by 
+ *
+ * Authors: Michael Zucchi <notzed@helixcode.com>
+ *          Jeffrey Stedfast <fejj@helixcode.com>
+ *
+ * Copyright 2000 Helix Code, Inc. (www.helixcode.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+/* this is needed for strptime() */
+#if !defined (__FreeBSD__)
+#define _XOPEN_SOURCE 600 /* glibc2 needs this */
+#else
+#define _XOPEN_SOURCE
+#endif
+
+#ifdef USE_PTHREAD
+#include <pthread.h>
+#endif
+
+#include <time.h>
+#include <glib.h>
+#include <locale.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "procheader.h"
+
+/* converts a ISO 8601 time string to a time_t value */
+time_t parseISO8601Date(gchar *date) {
+       struct tm       tm;
+       time_t          t, t2, offset = 0;
+       gboolean        success = FALSE;
+#ifdef G_OS_WIN32
+       gchar *tmp = g_strdup(date);
+       gint result, year, month, day, hour, minute, second;
+#else
+       gchar *pos;
+#endif 
+       g_assert(date != NULL);
+       
+       memset(&tm, 0, sizeof(struct tm));
+       
+#ifdef G_OS_WIN32
+       g_strstrip(tmp);
+       result = sscanf((const char *)date, "%d-%d-%dT%d:%d:%d", 
+                       &year, &month, &day, &hour, &minute, &second);
+       if (result < 6)
+               second = 0;
+       if (result < 5)
+               minute = 0;
+       if (result < 4)
+               hour = 0;
+       if (result >= 3) {
+               tm.tm_sec = second;
+               tm.tm_min = minute;
+               tm.tm_hour = hour;
+               tm.tm_mday = day;
+               tm.tm_mon = month - 1;
+               tm.tm_year = year - 1900;
+               tm.tm_wday = 0;
+               tm.tm_yday = 0;
+               tm.tm_isdst = -1;
+               success = TRUE;
+       }
+#else
+       /* we expect at least something like "2003-08-07T15:28:19" and
+          don't require the second fractions and the timezone info
+
+          the most specific format:   YYYY-MM-DDThh:mm:ss.sTZD
+        */
+        
+       /* full specified variant */
+       if(NULL != (pos = strptime((const char *)date, "%t%Y-%m-%dT%H:%M%t", &tm))) {
+               /* Parse seconds */
+               if (*pos == ':')
+                       pos++;
+               if (isdigit(pos[0]) && !isdigit(pos[1])) {
+                       tm.tm_sec = pos[0] - '0';
+                       pos++;
+               } else if (isdigit(pos[0]) && isdigit(pos[1])) {
+                       tm.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
+                       pos +=2;
+               }
+               /* Parse timezone */
+               if (*pos == 'Z')
+                       offset = 0;
+               else if ((*pos == '+' || *pos == '-') && isdigit(pos[1]) && isdigit(pos[2]) && strlen(pos) >= 3) {
+                       offset = (10*(pos[1] - '0') + (pos[2] - '0')) * 60 * 60;
+                       
+                       if (pos[3] == ':' && isdigit(pos[4]) && isdigit(pos[5]))
+                               offset +=  (10*(pos[4] - '0') + (pos[5] - '0')) * 60;
+                       else if (isdigit(pos[3]) && isdigit(pos[4]))
+                               offset +=  (10*(pos[3] - '0') + (pos[4] - '0')) * 60;
+                       
+                       offset *= (pos[0] == '+') ? 1 : -1;
+
+               }
+               success = TRUE;
+       /* only date */
+       } else if(NULL != strptime((const char *)date, "%t%Y-%m-%d", &tm))
+               success = TRUE;
+       /* there were others combinations too... */
+#endif
+       if(TRUE == success) {
+               if((time_t)(-1) != (t = mktime(&tm))) {
+                       struct tm buft;
+                       /* Correct for the local timezone*/
+                       t = t - offset;
+                       t2 = mktime(gmtime_r(&t, &buft));
+                       t = t - (t2 - t);
+                       
+                       return t;
+               } else {
+                       g_warning("internal error! time conversion error! mktime failed!\n");
+               }
+       } else {
+               g_warning("Invalid ISO8601 date format! Ignoring <dc:date> information!\n");
+       }
+       
+       return 0;
+}
+
+gchar *dayofweek[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+gchar *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+gchar *createRFC822Date(const time_t *time) {
+       struct tm *tm;
+       struct tm buft;
+#ifdef G_OS_WIN32
+       if (*time < 0) {
+               time_t t = 1;
+               tm = gmtime_r(&t, &buft);
+       } else 
+#endif
+       {
+               tm = gmtime_r(time, &buft); /* No need to free because it is statically allocated */
+       }
+       return g_strdup_printf("%s, %2d %s %4d %02d:%02d:%02d GMT", dayofweek[tm->tm_wday], tm->tm_mday,
+                                          months[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
diff --git a/src/plugins/rssyl/date.h b/src/plugins/rssyl/date.h
new file mode 100644 (file)
index 0000000..27c1353
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __DATE_H
+#define __DATE_H
+
+#include <time.h>
+#include <glib.h>
+
+time_t parseISO8601Date(gchar *date);
+gchar *createRFC822Date(const time_t *time);
+
+#endif /* __DATE_H */
diff --git a/src/plugins/rssyl/feed.c b/src/plugins/rssyl/feed.c
new file mode 100644 (file)
index 0000000..35823b6
--- /dev/null
@@ -0,0 +1,1864 @@
+/*
+ * 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@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;
+};
+
+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
+       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, 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;
+
+       *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[] = {
+       { "&lt;", "<" },
+       { "&gt;", ">" },
+       { "&amp;", "&" },
+       { "&quot;", "\"" },
+       { "&lsquo;",  "'" },
+       { "&rsquo;",  "'" },
+       { "&ldquo;",  "\"" },
+       { "&rdquo;",  "\"" },
+       { "&nbsp;", " " },
+       { "&trade;", "(TM)" },
+       { "&#153;", "(TM)" },
+       { "&#39;", "'" },
+       { "&hellip;", "..." },
+       { "&mdash;", "-" },
+       { "<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, &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, &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, &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
new file mode 100644 (file)
index 0000000..21b0745
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef __FEED_H
+#define __FEED_H
+
+#include <libxml/parser.h>
+
+#include "procmsg.h"
+
+#include "rssyl.h"
+
+#define RSSYL_TMP_TEMPLATE     "curltmpXXXXXX"
+
+#define RSSYL_XPATH_ROOT               "/rssyl"
+#define RSSYL_XPATH_TITLE              RSSYL_XPATH_ROOT"/title"
+#define RSSYL_XPATH_LINK               RSSYL_XPATH_ROOT"/link"
+#define RSSYL_XPATH_TEXT               RSSYL_XPATH_ROOT"/text"
+
+#define RSSYL_TEXT_START               "<!-- RSSyl text start -->"
+#define RSSYL_TEXT_END                 "<!-- RSSyl text end -->"
+
+#define RSSYL_LOG_ERROR_TIMEOUT                _("Time out connecting to URL %s\n")
+#define RSSYL_LOG_ERROR_FETCH          _("Couldn't fetch URL %s\n")
+#define RSSYL_LOG_ERROR_PARSE          _("Error parsing feed from URL %s\n")
+#define RSSYL_LOG_ERROR_UNKNOWN                _("Unsupported feed type at URL %s\n")
+
+#define RSSYL_LOG_UPDATING             _("RSSyl: Updating feed %s\n")
+#define RSSYL_LOG_UPDATED              _("RSSyl: Feed update finished: %s\n")
+#define RSSYL_LOG_EXITING                      _("RSSyl: Feed update aborted, application is exiting.\n")
+
+struct _RSSylFeedItemMedia {
+       gchar *url;
+       gchar *type;
+       gulong size;
+};
+
+typedef struct _RSSylFeedItemMedia RSSylFeedItemMedia;
+
+struct _RSSylFeedItem {
+       gchar *title;
+       gchar *text;
+       gchar *link;
+       gchar *parent_link;
+       gchar *comments_link;
+       gchar *author;
+       gchar *id;
+       gboolean id_is_permalink;
+
+       RSSylFeedItemMedia *media;
+
+#ifdef RSSYL_DEBUG
+       long int debug_fetched;
+#endif /* RSSYL_DEBUG */
+
+       gchar *realpath;
+       time_t date;
+       time_t date_published;
+};
+
+typedef struct _RSSylFeedItem RSSylFeedItem;
+
+struct _RSSylFindByUrlCtx {
+       gchar *url;
+       RSSylFolderItem *ritem;
+};
+
+typedef struct _RSSylFindByUrlCtx RSSylFindByUrlCtx;
+
+struct _RSSylParseCtx {
+       RSSylFolderItem *ritem;
+       gboolean ready;
+};
+
+typedef struct _RSSylParseCtx RSSylParseCtx;
+
+xmlDocPtr rssyl_fetch_feed(const gchar *url, time_t last_update, gchar **title, gchar **error);
+void rssyl_parse_feed(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent);
+gboolean rssyl_add_feed_item(RSSylFolderItem *ritem, RSSylFeedItem *fitem);
+MsgInfo *rssyl_parse_feed_item_to_msginfo(gchar *file, MsgFlags flags,
+               gboolean a, gboolean b, FolderItem *item);
+void rssyl_remove_feed_cache(FolderItem *item);
+void rssyl_update_feed(RSSylFolderItem *ritem);
+void rssyl_read_existing(RSSylFolderItem *ritem);
+
+void rssyl_start_refresh_timeout(RSSylFolderItem *ritem);
+void rssyl_expire_items(RSSylFolderItem *ritem);
+
+FolderItem *rssyl_subscribe_new_feed(FolderItem *parent, const gchar *url, gboolean verbose);
+void rssyl_free_feeditem(RSSylFeedItem *item);
+gchar *rssyl_format_string(const gchar *str, gboolean replace_html, gboolean replace_returns);
+
+void rssyl_refresh_all_func(FolderItem *item, gpointer data);
+void rssyl_refresh_all_feeds(void);
+gchar *rssyl_feed_title_to_dir(const gchar *title);
+
+#endif /* __FEED_H */
diff --git a/src/plugins/rssyl/feedprops.c b/src/plugins/rssyl/feedprops.c
new file mode 100644 (file)
index 0000000..3b8f651
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - functions for handling feeds.xml file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "folder.h"
+
+#include "feed.h"
+#include "feedprops.h"
+#include "rssyl.h"
+#include "rssyl_prefs.h"
+
+static gchar *rssyl_get_props_path(void)
+{
+       return g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+                       G_DIR_SEPARATOR_S, RSSYL_PROPS_FILE, NULL);
+}
+
+
+/* rssyl_store_feed_props()
+ *
+ * Stores feed properties into feeds.xml file in the rcdir. Updates if already
+ * stored for this feed.
+ */
+void rssyl_store_feed_props(RSSylFolderItem *ritem)
+{
+       gchar *path;
+       xmlDocPtr doc;
+       xmlNodePtr node, rootnode;
+       xmlXPathObjectPtr result;
+       xmlXPathContextPtr context;
+       FolderItem *item = &ritem->item;
+       gboolean found = FALSE, def_ri, def_ex;
+       gint i;
+
+       g_return_if_fail(ritem != NULL);
+       g_return_if_fail(ritem->url != NULL);
+
+       def_ri = ritem->default_refresh_interval;
+       if( def_ri )
+               ritem->refresh_interval = rssyl_prefs_get()->refresh;
+
+       def_ex = ritem->default_expired_num;
+       if( def_ex )
+               ritem->expired_num = rssyl_prefs_get()->expired;
+
+       path = rssyl_get_props_path();
+
+       if( !(doc = xmlParseFile(path)) ) {
+               debug_print("RSSyl: file %s doesn't exist, creating it\n", path);
+               doc = xmlNewDoc("1.0");
+
+               rootnode = xmlNewNode(NULL, "feeds");
+               xmlDocSetRootElement(doc, rootnode);
+       } else {
+               rootnode = xmlDocGetRootElement(doc);
+       }
+
+       context = xmlXPathNewContext(doc);
+       if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
+               debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
+               xmlXPathFreeContext(context);
+       } else {
+               for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
+                       gchar *tmp, *t_prop = NULL;
+                       node = result->nodesetval->nodeTab[i];
+                       tmp = xmlGetProp(node, RSSYL_PROP_NAME);
+
+                       if( !strcmp(tmp, item->name) ) {
+                               debug_print("RSSyl: XML - updating node for '%s'\n", item->name);
+
+                               xmlSetProp(node, RSSYL_PROP_NAME, item->name);
+                               xmlSetProp(node, RSSYL_PROP_OFFICIAL_NAME, 
+                                               ritem->official_name?ritem->official_name:item->name);
+                               xmlSetProp(node, RSSYL_PROP_URL, ritem->url);
+
+                               xmlSetProp(node, RSSYL_PROP_DEF_REFRESH, (def_ri ? "1" : "0") );
+                               if( !def_ri ) {
+                                       t_prop = g_strdup_printf("%d", ritem->refresh_interval);
+                                       xmlSetProp(node, RSSYL_PROP_REFRESH,
+                                                       t_prop );
+                                       g_free(t_prop);
+                               }
+
+                               xmlSetProp(node, RSSYL_PROP_DEF_EXPIRED, (def_ex ? "1" : "0") );
+                               if( !def_ex ) {
+                                       t_prop = g_strdup_printf("%d", ritem->expired_num);
+                                       xmlSetProp(node, RSSYL_PROP_EXPIRED,
+                                                       t_prop );
+                                       g_free(t_prop);
+                               }
+                               xmlSetProp(node, RSSYL_PROP_FETCH_COMMENTS,
+                                               (ritem->fetch_comments ? "1" : "0"));
+                               t_prop = g_strdup_printf("%d", ritem->fetch_comments_for);
+                               xmlSetProp(node, RSSYL_PROP_FETCH_COMMENTS_FOR, t_prop);
+                               g_free(t_prop);
+
+                               t_prop = g_strdup_printf("%d", ritem->silent_update);
+                               xmlSetProp(node, RSSYL_PROP_SILENT_UPDATE, t_prop);
+                               g_free(t_prop);
+
+                               found = TRUE;
+                       }
+                       xmlFree(tmp);
+               }
+       }
+
+       xmlXPathFreeContext(context);
+       xmlXPathFreeObject(result);
+
+       /* node for our feed doesn't exist, let's make one */
+       if( !found ) {
+               debug_print("RSSyl: XML - creating node for '%s', storing URL '%s'\n",
+                               item->name, ritem->url);
+               node = xmlNewTextChild(rootnode, NULL, "feed", NULL);
+               xmlSetProp(node, RSSYL_PROP_NAME, item->name);
+               xmlSetProp(node, RSSYL_PROP_OFFICIAL_NAME, 
+                               ritem->official_name?ritem->official_name:item->name);
+               xmlSetProp(node, RSSYL_PROP_URL, ritem->url);
+               xmlSetProp(node, RSSYL_PROP_DEF_REFRESH, (def_ri ? "1" : "0") );
+               if( !def_ri )
+                       xmlSetProp(node, RSSYL_PROP_REFRESH,
+                                       g_strdup_printf("%d", ritem->refresh_interval) );
+               xmlSetProp(node, RSSYL_PROP_DEF_EXPIRED, (def_ex ? "1" : "0") );
+               if( !def_ex )
+                       xmlSetProp(node, RSSYL_PROP_EXPIRED,
+                                       g_strdup_printf("%d", ritem->expired_num) );
+       }
+
+       xmlSaveFormatFile(path, doc, 1);
+       xmlFreeDoc(doc);
+       g_free(path);
+}
+
+/* rssyl_get_feed_props()
+ *
+ * Retrieves props for feed from feeds.xml file in the rcdir.
+ */
+void rssyl_get_feed_props(RSSylFolderItem *ritem)
+{
+       gchar *path, *tmp = NULL;
+       xmlDocPtr doc;
+       xmlNodePtr node;
+       xmlXPathObjectPtr result;
+       xmlXPathContextPtr context;
+       FolderItem *item = &ritem->item;
+       gint i, tmpi;
+       gboolean force_update = FALSE;
+       RSSylPrefs *rsprefs = NULL;
+
+       g_return_if_fail(ritem != NULL);
+
+       if( ritem->url ) {
+               g_free(ritem->url);
+               ritem->url = NULL;
+       }
+
+       ritem->default_refresh_interval = TRUE;
+       ritem->refresh_interval = rssyl_prefs_get()->refresh;
+       ritem->expired_num = rssyl_prefs_get()->expired;
+
+       path = rssyl_get_props_path();
+
+       doc = xmlParseFile(path);
+       g_return_if_fail(doc != NULL);
+
+       context = xmlXPathNewContext(doc);
+       if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
+               debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
+               xmlXPathFreeContext(context);
+       } else {
+               for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
+                       gchar *property = NULL;
+                       node = result->nodesetval->nodeTab[i];
+                       property = xmlGetProp(node, RSSYL_PROP_NAME);
+                       if( !strcmp(property, item->name) ) {
+                               /* official name */
+                               tmp = xmlGetProp(node, RSSYL_PROP_OFFICIAL_NAME);
+                               ritem->official_name = (tmp ? g_strdup(tmp) : g_strdup(item->name));
+                               if (tmp == NULL)
+                                       force_update = TRUE;
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* URL */
+                               tmp = xmlGetProp(node, RSSYL_PROP_URL);
+                               ritem->url = (tmp ? g_strdup(tmp) : NULL);
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               tmp = xmlGetProp(node, RSSYL_PROP_DEF_REFRESH);
+                               tmpi = 0;
+                               if( tmp )
+                                       tmpi = atoi(tmp);
+                               ritem->default_refresh_interval = (tmpi ? TRUE : FALSE );
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* refresh_interval */
+                               tmp = xmlGetProp(node, RSSYL_PROP_REFRESH);
+                               if( ritem->default_refresh_interval )
+                                       ritem->refresh_interval = rssyl_prefs_get()->refresh;
+                               else {
+                                       tmpi = -1;
+                                       if( tmp )
+                                               tmpi = atoi(tmp);
+
+                                       ritem->refresh_interval =
+                                               (tmpi != -1 ? tmpi : rssyl_prefs_get()->refresh);
+                               }
+                               
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* expired_num */
+                               tmp = xmlGetProp(node, RSSYL_PROP_DEF_EXPIRED);
+                               tmpi = 0;
+                               if( tmp ) {
+                                       tmpi = atoi(tmp);
+                                       ritem->default_expired_num = tmpi;
+                               }
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* fetch_comments */
+                               tmp = xmlGetProp(node, RSSYL_PROP_FETCH_COMMENTS);
+                               tmpi = 0;
+                               if( tmp ) {
+                                       tmpi = atoi(tmp);
+                                       ritem->fetch_comments = tmpi;
+                               }
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* fetch_comments_for */
+                               tmp = xmlGetProp(node, RSSYL_PROP_FETCH_COMMENTS_FOR);
+                               tmpi = 0;
+                               if( tmp ) {
+                                       tmpi = atoi(tmp);
+                                       ritem->fetch_comments_for = tmpi;
+                               }
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               /* silent_update */
+                               tmp = xmlGetProp(node, RSSYL_PROP_SILENT_UPDATE);
+                               tmpi = 0;
+                               if( tmp ) {
+                                       tmpi = atoi(tmp);
+                                       ritem->silent_update = tmpi;
+                               }
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               tmp = xmlGetProp(node, RSSYL_PROP_EXPIRED);
+
+                               if( ritem->default_expired_num )
+                                       ritem->expired_num = rssyl_prefs_get()->expired;
+                               else {
+                                       tmpi = -2;
+                                       if( tmp )
+                                               tmpi = atoi(tmp);
+
+                                       ritem->expired_num = (tmpi != -2 ? tmpi : rssyl_prefs_get()->expired);
+                               }
+
+                               xmlFree(tmp);
+                               tmp = NULL;
+
+                               debug_print("RSSyl: XML - props for '%s' loaded\n", item->name);
+
+                               /* Start automatic refresh timer, if necessary */
+                               if( ritem->refresh_id == 0 ) {
+                                       /* Check if user wants the default for this feed */
+                                       if( ritem->default_refresh_interval ) {
+                                               rsprefs = rssyl_prefs_get();
+                                               ritem->refresh_interval = rsprefs->refresh;
+                                       }
+
+                                       /* Start the timer, if determined interval is >0 */
+                                       if( ritem->refresh_interval >= 0 )
+                                               rssyl_start_refresh_timeout(ritem);
+                               }
+                       }
+                       xmlFree(property);
+               }
+       }
+
+       xmlXPathFreeObject(result);
+       xmlXPathFreeContext(context);
+       xmlFreeDoc(doc);
+       g_free(path);
+       if (force_update)
+               rssyl_store_feed_props(ritem);
+}
+
+void rssyl_remove_feed_props(RSSylFolderItem *ritem)
+{
+       gchar *path;
+       xmlDocPtr doc;
+       xmlNodePtr node;
+       xmlXPathObjectPtr result;
+       xmlXPathContextPtr context;
+       FolderItem *item = &ritem->item;
+       gint i;
+
+       g_return_if_fail(ritem != NULL);
+
+       path = rssyl_get_props_path();
+
+       doc = xmlParseFile(path);
+       g_return_if_fail(doc != NULL);
+
+       context = xmlXPathNewContext(doc);
+       if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
+               debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
+               xmlXPathFreeContext(context);
+       } else {
+               for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
+                       gchar *tmp;
+                       node = result->nodesetval->nodeTab[i];
+                       tmp = xmlGetProp(node, RSSYL_PROP_NAME);
+                       if( !strcmp(tmp, item->name) ) {
+                               debug_print("RSSyl: XML - found node for '%s', removing\n", item->name);
+                               xmlUnlinkNode(node);
+                       }
+                       xmlFree(tmp);
+               }
+       }
+
+       xmlXPathFreeObject(result);
+       xmlXPathFreeContext(context);
+
+       xmlSaveFormatFile(path, doc, 1);
+
+       xmlFreeDoc(doc);
+       g_free(path);
+}
+
+void rssyl_props_update_name(RSSylFolderItem *ritem, gchar *new_name)
+{
+       gchar *path;
+       xmlDocPtr doc;
+       xmlNodePtr node, rootnode;
+       xmlXPathObjectPtr result;
+       xmlXPathContextPtr context;
+       FolderItem *item = &ritem->item;
+       gboolean found = FALSE;
+       gint i;
+
+       g_return_if_fail(ritem != NULL);
+       g_return_if_fail(ritem->url != NULL);
+
+       path = rssyl_get_props_path();
+
+       if( !(doc = xmlParseFile(path)) ) {
+               debug_print("RSSyl: file %s doesn't exist, creating it\n", path);
+               doc = xmlNewDoc("1.0");
+
+               rootnode = xmlNewNode(NULL, "feeds");
+               xmlDocSetRootElement(doc, rootnode);
+       } else {
+               rootnode = xmlDocGetRootElement(doc);
+       }
+
+       context = xmlXPathNewContext(doc);
+       if( !(result = xmlXPathEvalExpression(RSSYL_PROPS_XPATH, context)) ) {
+               debug_print("RSSyl: XML - no result found for %s\n", RSSYL_PROPS_XPATH);
+               xmlXPathFreeContext(context);
+       } else {
+               for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
+                       gchar *tmp;
+                       node = result->nodesetval->nodeTab[i];
+                       tmp = xmlGetProp(node, RSSYL_PROP_NAME);
+                       if( !strcmp(tmp, item->name) ) {
+                               debug_print("RSSyl: XML - updating node for '%s'\n", item->name);
+                               xmlSetProp(node, "name", new_name);
+                               found = TRUE;
+                       }
+                       xmlFree(tmp);
+               }
+       }
+
+       xmlXPathFreeContext(context);
+       xmlXPathFreeObject(result);
+
+       if( !found )
+               debug_print("couldn't find feed\n");
+
+       xmlSaveFormatFile(path, doc, 1);
+       xmlFreeDoc(doc);
+       g_free(path);
+}
diff --git a/src/plugins/rssyl/feedprops.h b/src/plugins/rssyl/feedprops.h
new file mode 100644 (file)
index 0000000..f8ff498
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __FEEDPROPS_H
+#define __FEEDPROPS_H
+
+#include "rssyl.h"
+
+#define RSSYL_PROPS_FILE       "feeds.xml"
+#define RSSYL_PROPS_XPATH      "/feeds/feed"
+
+#define RSSYL_PROP_URL                                 "url"
+#define RSSYL_PROP_NAME                                        "name"
+#define RSSYL_PROP_OFFICIAL_NAME                       "official_name"
+#define RSSYL_PROP_DEF_REFRESH "default_refresh_interval"
+#define RSSYL_PROP_REFRESH                     "refresh_interval"
+#define RSSYL_PROP_DEF_EXPIRED "default_expired_num"
+#define RSSYL_PROP_EXPIRED                     "expired_num"
+#define RSSYL_PROP_FETCH_COMMENTS              "fetch_comments"
+#define RSSYL_PROP_FETCH_COMMENTS_FOR          "fetch_comments_for"
+#define RSSYL_PROP_SILENT_UPDATE "silent_update"
+
+void rssyl_store_feed_props(RSSylFolderItem *ritem);
+void rssyl_get_feed_props(RSSylFolderItem *ritem);
+void rssyl_remove_feed_props(RSSylFolderItem *ritem);
+void rssyl_props_update_name(RSSylFolderItem *ritem, gchar *new_name);
+
+#endif /* __FEEDPROPS_H */
diff --git a/src/plugins/rssyl/opml.c b/src/plugins/rssyl/opml.c
new file mode 100644 (file)
index 0000000..c71cf8b
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Claws-Mail-- a GTK+ based, lightweight, and fast e-mail client
+ * This file (C) 2008 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - Export feed list to OPML
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <errno.h>
+#include <glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "log.h"
+#include "folder.h"
+#include "folderview.h"
+
+#include "date.h"
+#include "feed.h"
+#include "rssyl.h"
+#include "strreplace.h"
+
+#define RSSYL_OPML_FILE        "rssyl-feedlist.opml"
+
+static gint _folder_depth(FolderItem *item)
+{
+       gint i;
+
+       for( i = 0; item != NULL; item = folder_item_parent(item), i++ ) {}
+       return i;
+}
+
+struct _RSSylOpmlExportCtx {
+       FILE *f;
+       gint depth;
+};
+
+typedef struct _RSSylOpmlExportCtx RSSylOpmlExportCtx;
+
+static void rssyl_opml_export_func(FolderItem *item, gpointer data)
+{
+       RSSylOpmlExportCtx *ctx = (RSSylOpmlExportCtx *)data;
+       RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+       gboolean isfolder = FALSE, err = FALSE;
+       gboolean haschildren = FALSE;
+       gchar *indent = NULL, *xmlurl = NULL;
+       gchar *tmpoffn = NULL, *tmpurl = NULL, *tmpname = NULL;
+       gint depth;
+
+       if( !IS_RSSYL_FOLDER_ITEM(item) )
+               return;
+
+       if( folder_item_parent(item) == NULL )
+               return;
+
+       /* Check for depth and adjust indentation */
+       depth = _folder_depth(item);
+       if( depth < ctx->depth ) {
+               for( ctx->depth--; depth <= ctx->depth; ctx->depth-- ) {
+                       indent = g_strnfill(ctx->depth, '\t');
+                       err |= (fprintf(ctx->f, "%s</outline>\n", indent) < 0);
+                       g_free(indent);
+               }
+       }
+       ctx->depth = depth;
+
+       if( ritem->url == NULL ) {
+               isfolder = TRUE;
+       } else {
+               tmpurl = rssyl_strreplace(ritem->url, "&", "&amp;");
+               xmlurl = g_strdup_printf("xmlUrl=\"%s\"", tmpurl);
+               g_free(tmpurl);
+       }
+
+       if( g_node_n_children(item->node) )
+               haschildren = TRUE;
+
+       indent = g_strnfill(ctx->depth, '\t');
+
+       tmpname = rssyl_strreplace(item->name, "&", "&amp;");
+       if (ritem->official_name != NULL)
+               tmpoffn = rssyl_strreplace(item->name, "&", "&amp;");
+       else
+               tmpoffn = g_strdup(tmpname);
+
+       err |= (fprintf(ctx->f,
+                               "%s<outline title=\"%s\" text=\"%s\" description=\"%s\" type=\"%s\" %s%s>\n",
+                               indent, tmpname, tmpoffn, tmpoffn,
+                               (isfolder ? "folder" : "rss"),
+                               (xmlurl ? xmlurl : ""), (haschildren ? "" : "/")) < 0);
+
+       g_free(indent);
+       g_free(xmlurl);
+       g_free(tmpname);
+       g_free(tmpoffn);
+       
+       if( err ) {
+               log_warning(LOG_PROTOCOL,
+                               "Error while writing '%s' to feed export list.\n",
+                               item->name);
+               debug_print("Error while writing '%s' to feed_export list.\n",
+                               item->name);
+       }
+}
+
+void rssyl_opml_export(void)
+{
+       FILE *f;
+       gchar *opmlfile, *tmpdate, *indent;
+       time_t tt = time(NULL);
+       RSSylOpmlExportCtx *ctx = NULL;
+       gboolean err = FALSE;
+
+       opmlfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+                       G_DIR_SEPARATOR_S, RSSYL_OPML_FILE, NULL);
+
+       if( g_file_test(opmlfile, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR ) )
+               g_remove(opmlfile);
+       
+       if( (f = g_fopen(opmlfile, "w")) == NULL ) {
+               log_warning(LOG_PROTOCOL,
+                               "Couldn't open file '%s' for feed list exporting: %s\n",
+                               opmlfile, g_strerror(errno));
+               debug_print("Couldn't open feed list export file, returning.\n");
+               g_free(opmlfile);
+               return;
+       }
+
+       tmpdate = createRFC822Date(&tt);
+
+       /* Write OPML header */
+       err |= (fprintf(f,
+                               "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                               "<opml version=\"1.1\">\n"
+                               "\t<head>\n"
+                               "\t\t<title>RSSyl Feed List Export</title>\n"
+                               "\t\t<dateCreated>%s</dateCreated>\n"
+                               "\t</head>\n"
+                               "\t<body>\n",
+                               tmpdate) < 0);
+       g_free(tmpdate);
+
+       ctx = g_new0(RSSylOpmlExportCtx, 1);
+       ctx->f = f;
+       ctx->depth = 1;
+
+       folder_func_to_all_folders(
+                       (FolderItemFunc)rssyl_opml_export_func, ctx);
+
+       for( ctx->depth--; ctx->depth >= 2; ctx->depth-- ) {
+               indent = g_strnfill(ctx->depth, '\t');
+               err |= (fprintf(ctx->f, "%s</outline>\n", indent) < 0);
+               g_free(indent);
+       }
+
+       err |= (fprintf(f,
+                               "\t</body>\n"
+                               "</opml>\n") < 0);
+
+       if( err ) {
+               log_warning(LOG_PROTOCOL, "Error during writing feed export file.\n");
+               debug_print("Error during writing feed export file.");
+       }
+
+       debug_print("Feed export finished.\n");
+
+       fclose(f);
+       g_free(opmlfile);
+       g_free(ctx);
+}
+
+static void rssyl_opml_import_node(xmlNodePtr node,
+               FolderItem *parent, gint depth)
+{
+       xmlNodePtr curn;
+       gchar *url = NULL, *title = NULL, *nodename = NULL;
+       FolderItem *item = NULL;
+
+       if( node == NULL )
+               return;
+
+       for( curn = node; curn; curn = curn->next ) {
+               nodename = g_ascii_strdown((gchar *)curn->name, -1);
+               if( curn->type == XML_ELEMENT_NODE &&
+                               !strcmp(nodename, "outline") ) {
+
+                       url = (gchar *)xmlGetProp(curn, (xmlChar *)"xmlUrl");
+                       title = (gchar *)xmlGetProp(curn, (xmlChar *)"title");
+                       if (!title)
+                               title = (gchar *)xmlGetProp(curn, (xmlChar *)"text");
+                       
+                       debug_print("Adding '%s' (%s)\n", title, (url ? url : "folder") );
+                       if( url != NULL )
+                               item = rssyl_subscribe_new_feed(parent, url, FALSE);
+                       else if (title != NULL)
+                               item = folder_create_folder(parent, title);
+                       else
+                               item = NULL;
+                       if (item)
+                               rssyl_opml_import_node(curn->children, item, depth + 1);
+               }
+               g_free(nodename);
+       }
+}
+
+void rssyl_opml_import(const gchar *opmlfile, FolderItem *parent)
+{
+       xmlDocPtr doc;
+       xmlNodePtr node;
+       xmlXPathContextPtr context;
+       xmlXPathObjectPtr result;
+       gchar *rootnode = NULL;
+
+       doc = xmlParseFile(opmlfile);
+       if( doc == NULL )
+               return;
+
+       node = xmlDocGetRootElement(doc);
+       rootnode = g_ascii_strdown((gchar *)node->name, -1);
+       if( !strcmp(rootnode, "opml") ) {
+               gchar *xpath = "/opml/body";
+               context = xmlXPathNewContext(doc);
+               if( !(result = xmlXPathEval((xmlChar *)xpath, context)) ) {
+                       g_free(rootnode);
+                       xmlFreeDoc(doc);
+                       return;
+               }
+
+               node = result->nodesetval->nodeTab[0];
+
+               debug_print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+               rssyl_opml_import_node(node->children, parent, 2);
+               debug_print("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+
+               xmlXPathFreeNodeSetList(result);
+               xmlXPathFreeContext(context);
+               xmlFreeDoc(doc);
+       }
+
+       g_free(rootnode);
+}
diff --git a/src/plugins/rssyl/opml.h b/src/plugins/rssyl/opml.h
new file mode 100644 (file)
index 0000000..9a8aef2
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __RSSYL_OPML
+#define __RSSYL_OPML
+
+#include "rssyl.h"
+
+void rssyl_opml_export(void);
+void rssyl_opml_import(const gchar *opmlfile, FolderItem *parent);
+
+#endif /* __RSSYL_OPML */
diff --git a/src/plugins/rssyl/parsers.c b/src/plugins/rssyl/parsers.c
new file mode 100644 (file)
index 0000000..33b50ec
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - various feed parsing functions
+ * - this file could use some sorting and/or splitting
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/HTMLtree.h>
+
+#include "date.h"
+#include "feed.h"
+#include "strreplace.h"
+#include "utils.h"
+#include "procheader.h"
+
+gint rssyl_parse_rdf(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
+{
+       xmlNodePtr rnode, node, n;
+       RSSylFeedItem *fitem = NULL;
+       gint count = 0;
+       gchar *content = NULL;
+       g_return_val_if_fail(doc != NULL, 0);
+       g_return_val_if_fail(ritem != NULL, 0);
+#ifdef RSSYL_DEBUG
+       gchar *fetched = NULL;
+#endif /* RSSYL_DEBUG */
+
+       if( ritem->contents == NULL )
+               rssyl_read_existing(ritem);
+
+       rnode = xmlDocGetRootElement(doc);
+
+       for( node = rnode->children; node; node = node->next ) {
+               if( !xmlStrcmp(node->name, "item") ) {
+                       /* We've found an "item" tag, let's poke through its contents */
+                       fitem = g_new0(RSSylFeedItem, 1);
+                       fitem->date = 0;
+#ifdef RSSYL_DEBUG
+                       fetched = xmlGetProp(rnode, "fetched");
+                       fitem->debug_fetched = atoll(fetched);
+                       xmlFree(fetched);
+#endif /* RSSYL_DEBUG */
+
+                       for( n = node->children; n; n = n->next ) {
+                               /* Title */
+                               if( !xmlStrcmp(n->name, "title") ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->title = rssyl_format_string(content, TRUE, TRUE);
+                                       xmlFree(content);
+                                       debug_print("RSSyl: XML - RDF title is '%s'\n", fitem->title);
+                               }
+
+                               /* Text */
+                               if( !xmlStrcmp(n->name, "description") ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->text = rssyl_format_string(content, FALSE, FALSE);
+                                       xmlFree(content);
+                                       debug_print("RSSyl: XML - got RDF text\n");
+                               }
+
+                               /* URL */
+                               if( !xmlStrcmp(n->name, "link") ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->link = rssyl_format_string(content, FALSE, TRUE);
+                                       xmlFree(content);
+                                       debug_print("RSSyl: XML - RDF link is '%s'\n", fitem->link);
+                               }
+
+                               /* Date - rfc822 format */
+                               if( !xmlStrcmp(n->name, "pubDate") ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->date = procheader_date_parse(NULL, content, 0);
+                                       xmlFree(content);
+                                       if( fitem->date > 0 ) {
+                                               debug_print("RSSyl: XML - RDF pubDate found\n" );
+                                       } else
+                                               fitem->date = 0;
+                               }
+                               /* Date - ISO8701 format */
+                               if( !xmlStrcmp(n->name, "date") &&
+                                               (!xmlStrcmp(n->ns->prefix, "ns")
+                                                || !xmlStrcmp(n->ns->prefix, "dc")) ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->date = parseISO8601Date(content);
+                                       xmlFree(content);
+                                       debug_print("RSSyl: XML - RDF date found\n" );
+                               }
+
+                               /* Author */
+                               if( !xmlStrcmp(n->name, "creator") ) {
+                                       content = xmlNodeGetContent(n);
+                                       fitem->author = rssyl_format_string(content, TRUE, TRUE);
+                                       xmlFree(content);
+                                       debug_print("RSSyl: XML - RDF author is '%s'\n", fitem->author);
+                               }
+                       }
+               }
+
+               if( fitem && fitem->link && fitem->title ) {
+                       if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
+                               rssyl_free_feeditem(fitem);
+                               fitem = NULL;
+                       }
+                       fitem = NULL;
+                       count++;
+               }
+       }
+
+       return count;
+}
+
+
+/* rssyl_parse_rss()
+ *
+ * This is where we parse the fetched rss document and create a
+ * RSSylFolderItem from it. Returns number of parsed items
+ */
+gint rssyl_parse_rss(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
+{
+       xmlXPathContextPtr context;
+       xmlXPathObjectPtr result;
+       xmlNodePtr node, n, rnode;
+       gint i, count = 0;
+       RSSylFeedItem *fitem = NULL;
+       gchar *xpath;
+       gboolean got_encoded, got_author;
+       gchar *rootnode = NULL;
+       RSSylFeedItemMedia *media;
+       gchar *media_url, *media_type;
+       gulong media_size = 0;
+#ifdef RSSYL_DEBUG
+       gchar *fetched = NULL;
+#endif /* RSSYL_DEBUG */
+
+       g_return_val_if_fail(doc != NULL, 0);
+       g_return_val_if_fail(ritem != NULL, 0);
+
+       if( ritem->contents == NULL )
+               rssyl_read_existing(ritem);
+
+       rnode = xmlDocGetRootElement(doc);
+
+       rootnode = g_ascii_strdown(rnode->name, -1);
+       xpath = g_strconcat("/", rootnode,
+                               "/channel/item",        NULL);
+       g_free(rootnode);
+       context = xmlXPathNewContext(doc);
+       if( !(result = xmlXPathEvalExpression(xpath, context)) ){
+               debug_print("RSSyl: XML - no result found for '%s'\n", xpath);
+               xmlXPathFreeContext(context);
+               g_free(xpath);
+               return 0;
+       }
+
+       g_free(xpath);
+
+       for( i = 0; i < result->nodesetval->nodeNr; i++ ) {
+               node = result->nodesetval->nodeTab[i];
+               
+               if ((n = node->children) == NULL)
+                       continue;
+
+               fitem = g_new0(RSSylFeedItem, 1);
+               fitem->media = NULL;
+               fitem->date = 0;
+#ifdef RSSYL_DEBUG
+               fetched = xmlGetProp(rnode, "fetched");
+               fitem->debug_fetched = atoll(fetched);
+               xmlFree(fetched);
+#endif /* RSSYL_DEBUG */
+               fitem->text = NULL;
+               
+               if (parent)
+                       fitem->parent_link = g_strdup(parent);
+
+               got_encoded = FALSE;
+               got_author = FALSE;
+               do {
+                       gchar *content = NULL;
+
+                       /* Title */
+                       if( !xmlStrcmp(n->name, "title") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->title = rssyl_format_string(content, TRUE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item title: '%s'\n", fitem->title);
+                       }
+
+                       /* Text */
+                       if( !xmlStrcmp(n->name, "description") ) {
+                               if( (fitem->text == NULL) && (got_encoded == FALSE) ) {
+                                       content = xmlNodeGetContent(n);
+                                       debug_print("RSSyl: XML - item text (description) caught\n");
+                                       fitem->text = rssyl_format_string(content, FALSE, FALSE);
+                                       xmlFree(content);
+                               }
+                       }
+                       if( !xmlStrcmp(n->name, "encoded")
+                                       && !xmlStrcmp(n->ns->prefix, "content") ) {
+                               debug_print("RSSyl: XML - item text (content) caught\n");
+
+                               if (fitem->text != NULL)
+                                       g_free(fitem->text); /* free "description" */
+                                       
+                               content = xmlNodeGetContent(n);
+                               fitem->text = rssyl_format_string(content, FALSE, FALSE);
+                               xmlFree(content);
+                               got_encoded = TRUE;
+                       }
+
+                       /* URL link to the original post */
+                       if( !xmlStrcmp(n->name, "link") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->link = rssyl_format_string(content, FALSE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item link: '%s'\n", fitem->link);
+                       }
+
+                       /* GUID - sometimes used as link */
+                       if( !xmlStrcmp(n->name, "guid") ) {
+                               gchar *tmp = xmlGetProp(n, "isPermaLink");
+                               content = xmlNodeGetContent(n);
+                               fitem->id_is_permalink = FALSE;
+                               if( !tmp || xmlStrcmp(tmp, "false") )   /* permalink? */
+                                       fitem->id_is_permalink = TRUE;
+                               fitem->id = rssyl_format_string(content, FALSE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item guid: '%s'\n", fitem->id);
+                               xmlFree(tmp);
+                       }
+
+                       /* Date - rfc822 format */
+                       if( !xmlStrcmp(n->name, "pubDate") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->date = procheader_date_parse(NULL, content, 0);
+                               xmlFree(content);
+                               if( fitem->date > 0 ) {
+                                       debug_print("RSSyl: XML - item date found: %d\n", (gint)fitem->date);
+                               } else
+                                       fitem->date = 0;
+                       }
+                       /* Date - ISO8701 format */
+                       if( !xmlStrcmp(n->name, "date") && !xmlStrcmp(n->ns->prefix, "dc") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->date = parseISO8601Date(content);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item date found\n" );
+                       }
+
+                       /* Author */
+                       if( !xmlStrcmp(n->name, "author") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->author = rssyl_format_string(content, TRUE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item author: '%s'\n", fitem->author);
+                               got_author = TRUE;
+                       }
+
+                       if( !xmlStrcmp(n->name, "creator")
+                                       && !xmlStrcmp(n->ns->prefix, "dc") && !got_author) {
+                               content = xmlNodeGetContent(n);
+                               fitem->author = rssyl_format_string(content, TRUE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - item author (creator): '%s'\n", fitem->author);
+                       }
+
+                       /* Media enclosure */
+                       if( !xmlStrcmp(n->name, "enclosure") ) {
+                               gchar *tmp = xmlGetProp(n, "length");
+                               media_url = xmlGetProp(n, "url");
+                               media_type = xmlGetProp(n, "type");
+                               media_size = (tmp ? atoi(tmp) : 0);
+                               xmlFree(tmp);
+
+                               if( media_url != NULL &&
+                                               media_type != NULL &&
+                                               media_size != 0 ) {
+                                       debug_print("RSSyl: XML - enclosure: '%s' [%s] (%ld)\n",
+                                                       media_url, media_type, media_size);
+                                       media = g_new(RSSylFeedItemMedia, 1);
+                                       media->url = media_url;
+                                       media->type = media_type;
+                                       media->size = media_size;
+                                       fitem->media = media;
+                               } else {
+                                       debug_print("RSSyl: XML - enclosure found, but some data is missing\n");
+                                       g_free(media_url);
+                                       g_free(media_type);
+                               }
+                       }
+
+                       /* Comments */
+                       if( !xmlStrcmp(n->name, "commentRSS") || !xmlStrcmp(n->name, "commentRss") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->comments_link = rssyl_format_string(content, FALSE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - comments_link: '%s'\n", fitem->comments_link);
+                       }
+               } while( (n = n->next) != NULL);
+
+               if( (fitem->link || fitem->id) && fitem->title ) {
+                       if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
+                               rssyl_free_feeditem(fitem);
+                               fitem = NULL;
+                       }
+                       count++;
+               }
+       }
+
+       xmlXPathFreeObject(result);
+       xmlXPathFreeContext(context);
+
+       return count;
+}
+
+/* rssyl_parse_atom()
+ *
+ * This is where we parse the fetched atom document and create a
+ * RSSylFolderItem from it. Returns number of parsed items
+ */
+gint rssyl_parse_atom(xmlDocPtr doc, RSSylFolderItem *ritem, gchar *parent)
+{
+       xmlNodePtr node, n, h;
+       xmlBufferPtr buf = NULL;
+       gint count = 0;
+       RSSylFeedItem *fitem = NULL;
+       RSSylFeedItemMedia *media = NULL;
+       gchar *link_type, *link_href, *link_rel, *tmp, *content = NULL;
+       gulong link_size;
+
+       g_return_val_if_fail(doc != NULL, 0);
+       g_return_val_if_fail(ritem != NULL, 0);
+
+       if( ritem->contents == NULL )
+               rssyl_read_existing(ritem);
+
+       node = xmlDocGetRootElement(doc);
+
+       if (node == NULL)
+               return 0;
+
+       node = node->children;
+
+       for (; node; node = node->next) {
+               gboolean got_content = FALSE;
+               if (xmlStrcmp(node->name, "entry")) {
+                       continue;
+               }
+       
+               n = node->children;
+               fitem = g_new0(RSSylFeedItem, 1);
+               fitem->date = 0;
+               fitem->date_published = 0;
+               fitem->text = NULL;
+               
+               if (parent)
+                       fitem->parent_link = g_strdup(parent);
+
+               do {
+                       /* Title */
+                       if( !xmlStrcmp(n->name, "title") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->title = rssyl_format_string(content, TRUE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - Atom item title: '%s'\n", fitem->title);
+                       }
+
+                       /* ID */
+                       if( !xmlStrcmp(n->name, "id") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->id = g_strdup_printf("%s%s", (parent?"comment-":""), content);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - Atom id: '%s'\n", fitem->id);
+                       }
+
+                       /* Text */
+                       if( !xmlStrcmp(n->name, "summary") && !got_content ) {
+                               content = xmlNodeGetContent(n);
+                               debug_print("RSSyl: XML - Atom item text (summary) caught\n");
+                               fitem->text = rssyl_format_string(content, FALSE, FALSE);
+                               xmlFree(content);
+                       }
+
+                       if( !xmlStrcmp(n->name, "content") ) {
+                               gchar *tmp = xmlGetProp(n, "type");
+                               debug_print("RSSyl: XML - Atom item text (content) caught\n");
+                               if (fitem->text)
+                                       g_free(fitem->text);
+                               if( !xmlStrcmp(tmp, "xhtml")) {
+                                       for( h = n->children; h; h = h->next ) {
+                                               if( !xmlStrcmp(h->name, "div") ) {
+                                                       buf = xmlBufferCreate();
+                                                       htmlNodeDump(buf, doc, h);
+                                                       content = g_strdup((gchar *)xmlBufferContent(buf));
+                                                       xmlBufferFree(buf);
+                                               }
+                                       }
+                               } else
+                                       content = xmlNodeGetContent(n);
+                               xmlFree(tmp);
+                               fitem->text = rssyl_format_string(content, FALSE, FALSE);
+                               xmlFree(content);
+                               got_content = TRUE;
+                       }
+
+                       /* link */
+                       if( !xmlStrcmp(n->name, "link") ) {
+                               link_type = xmlGetProp(n, "type");
+                               link_rel = xmlGetProp(n, "rel");
+                               link_href = xmlGetProp(n, "href");
+                               tmp = xmlGetProp(n, "length");
+                               link_size = (tmp ? atoi(tmp) : 0);
+                               g_free(tmp);
+
+                               if( !link_rel || (link_rel && !xmlStrcmp(link_rel, "alternate")) ) {
+                                       fitem->link = link_href;
+                                       debug_print("RSSyl: XML - Atom item link: '%s'\n", fitem->link);
+                                       xmlFree(link_type);
+                                       xmlFree(link_rel);
+                               } else if( link_rel && !xmlStrcmp(link_rel, "enclosure") ) {
+                                       debug_print("RSSyl: XML - Atom item enclosure: '%s' (%ld) [%s]\n",
+                                                       link_href, link_size, link_type);
+                                       media = g_new(RSSylFeedItemMedia, 1);
+                                       media->url = link_href;
+                                       media->type = link_type;
+                                       media->size = link_size;
+                                       fitem->media = media;
+                                       xmlFree(link_rel);
+                               } else {
+                                       xmlFree(link_type);
+                                       xmlFree(link_rel);
+                                       xmlFree(link_href);
+                               }
+                       }
+
+                       /* Date published - ISO8701 format */
+                       if( !xmlStrcmp(n->name, "published") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->date_published = parseISO8601Date(content);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - Atom item 'issued' date found\n" );
+                       }
+
+                       /* Date modified - ISO8701 format */
+                       if( !xmlStrcmp(n->name, "updated") ) {
+                               content = xmlNodeGetContent(n);
+                               fitem->date = parseISO8601Date(content);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - Atom item 'updated' date found\n" );
+                       }
+
+                       /* Author */
+                       if( !xmlStrcmp(n->name, "author") ) {
+                               xmlNodePtr subnode;
+                               gchar *name = NULL, *mail = NULL;
+                               gchar *tmp;
+                               for (subnode = n->children; subnode; subnode = subnode->next) {
+                                       content = xmlNodeGetContent(subnode);
+                                       if (!xmlStrcmp(subnode->name, "name") && !name)
+                                               name = g_strdup(content);
+                                       if (!xmlStrcmp(subnode->name, "email") && !mail)
+                                               mail = g_strdup(content);
+                                       xmlFree(content);
+                               }
+                               tmp = g_strdup_printf("%s%s%s%s%s",
+                                                       name ? name:"",
+                                                       name && mail ? " <":(mail?"<":""),
+                                                       mail ? mail:"",
+                                                       mail ? ">":"",
+                                                       !name && !mail ? "N/A":"");
+                               fitem->author = rssyl_format_string(tmp, TRUE, TRUE);
+                               g_free(tmp);
+                               g_free(name);
+                               g_free(mail);
+                               debug_print("RSSyl: XML - Atom item author: '%s'\n", fitem->author);
+                       }
+
+                       /* Comments */
+                       if( !xmlStrcmp(n->name, "commentRSS") || !xmlStrcmp(n->name, "commentRss")) {
+                               content = xmlNodeGetContent(n);
+                               fitem->comments_link = rssyl_format_string(content, FALSE, TRUE);
+                               xmlFree(content);
+                               debug_print("RSSyl: XML - comments_link: '%s'\n", fitem->comments_link);
+                       }
+               } while( (n = n->next) != NULL);
+
+               if( fitem->id && fitem->title && fitem->date ) {
+
+                       /* If no link is available, and we can safely guess ID
+                        * might be a (perma)link, mark it so. */
+                       if (!fitem->link && fitem->id   /* no url, but we have id */
+                                       && (!strncmp(fitem->id, "http:", 5) /* id looks like an url */
+                                               || !strncmp(fitem->id, "https:", 6))) {
+                               if (!ritem->url || strcmp(ritem->url, fitem->id)) {
+                                       /* id is different from feed url (good chance it is a permalink) */
+                                       debug_print("RSSyl: Marking ID as permalink\n");
+                                       fitem->id_is_permalink = TRUE;
+                               }
+                       }
+
+                       if (rssyl_add_feed_item(ritem, fitem) == FALSE) {
+                               rssyl_free_feeditem(fitem);
+                               fitem = NULL;
+                       }
+                       count++;
+               } else
+                       debug_print("RSSyl: Incomplete Atom entry, need at least 'id', 'title' and 'updated' tags\n");
+       }
+
+       return count;
+}
diff --git a/src/plugins/rssyl/parsers.h b/src/plugins/rssyl/parsers.h
new file mode 100644 (file)
index 0000000..48321e6
--- /dev/null
@@ -0,0 +1,13 @@
+#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/placeholder.txt b/src/plugins/rssyl/placeholder.txt
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/rssyl/plugin.c b/src/plugins/rssyl/plugin.c
new file mode 100644 (file)
index 0000000..9cf3cdc
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - generic s-c plugin stuff
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "common/version.h"
+#include "claws.h"
+#include <curl/curl.h>
+
+#include "rssyl.h"
+#include "plugin.h"
+
+gint plugin_init(gchar **error)
+{
+       if( !check_plugin_version(MAKE_NUMERIC_VERSION(3,7,8,31),
+                               VERSION_NUMERIC, PLUGIN_NAME, error) )
+               return -1;
+
+       curl_global_init(CURL_GLOBAL_DEFAULT);
+       rssyl_init();
+
+       return 0;
+}
+
+gboolean plugin_done(void)
+{
+       rssyl_done();
+       return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+       return PLUGIN_NAME;
+}
+
+const gchar *plugin_desc(void)
+{
+       return _("This plugin allows you to create a mailbox tree where you can add "
+               "newsfeeds in RSS 1.0, RSS 2.0 or Atom format.\n\n"
+               "Each newsfeed will create a folder with appropriate entries, fetched "
+               "from the web. You can read them, and delete or keep old entries.");
+}
+
+const gchar *plugin_type(void)
+{
+       return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+       return "GPL2+";
+}
+
+const gchar *plugin_version(void)
+{
+       return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+       static struct PluginFeature features[] = 
+               { {PLUGIN_FOLDERCLASS, N_("RSS feed")},
+                 {PLUGIN_NOTHING, NULL}};
+       return features;
+}
diff --git a/src/plugins/rssyl/plugin.def b/src/plugins/rssyl/plugin.def
new file mode 100644 (file)
index 0000000..8916a5d
--- /dev/null
@@ -0,0 +1,10 @@
+EXPORTS
+        plugin_desc
+        plugin_done
+        plugin_init
+        plugin_licence
+        plugin_name
+        plugin_type
+       plugin_provides
+        plugin_version
+
diff --git a/src/plugins/rssyl/rssyl.c b/src/plugins/rssyl/rssyl.c
new file mode 100644 (file)
index 0000000..282ffe4
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - s-c folderclass callback handler functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef G_OS_WIN32
+#  include <w32lib.h>
+#endif
+
+#include <glib.h>
+#include <curl/curl.h>
+
+#include "folder.h"
+#include "localfolder.h"
+
+#include "procheader.h"
+#include "common/utils.h"
+#include "toolbar.h"
+#include "prefs_toolbar.h"
+
+#include "main.h"
+
+#include "feed.h"
+#include "feedprops.h"
+#include "opml.h"
+#include "rssyl.h"
+#include "rssyl_gtk.h"
+#include "rssyl_prefs.h"
+#include "strreplace.h"
+
+static gint rssyl_create_tree(Folder *folder);
+
+static gboolean existing_tree_found = FALSE;
+
+static void rssyl_init_read_func(FolderItem *item, gpointer data)
+{
+       if( !IS_RSSYL_FOLDER_ITEM(item) )
+               return;
+
+       existing_tree_found = TRUE;
+
+       if( folder_item_parent(item) == NULL )
+               return;
+
+       rssyl_get_feed_props((RSSylFolderItem *)item);
+}
+
+static void rssyl_make_rc_dir(void)
+{
+       gchar *rssyl_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+                       NULL);
+
+       if( !is_dir_exist(rssyl_dir) ) {
+               if( make_dir(rssyl_dir) < 0 ) {
+                       g_warning("couldn't create directory %s\n", rssyl_dir);
+               }
+
+               debug_print("created directorty %s\n", rssyl_dir);
+       }
+
+       g_free(rssyl_dir);
+}
+
+static void rssyl_create_default_mailbox(void)
+{
+       Folder *root = NULL;
+       FolderItem *item;
+
+       rssyl_make_rc_dir();
+
+       root = folder_new(rssyl_folder_get_class(), RSSYL_DEFAULT_MAILBOX, NULL);
+
+       g_return_if_fail(root != NULL);
+       folder_add(root);
+
+       item = FOLDER_ITEM(root->node->data);
+
+       rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
+}
+
+static gboolean rssyl_refresh_all_feeds_deferred(gpointer data)
+{
+       rssyl_refresh_all_feeds();
+       return FALSE;
+}
+
+static void rssyl_toolbar_cb_refresh_all(gpointer parent, const gchar *item_name, gpointer data)
+{
+       rssyl_refresh_all_feeds();
+}
+
+void rssyl_init(void)
+{
+       folder_register_class(rssyl_folder_get_class());
+
+       rssyl_gtk_init();
+
+       rssyl_make_rc_dir();
+
+       rssyl_prefs_init();
+
+       folder_func_to_all_folders((FolderItemFunc)rssyl_init_read_func, NULL);
+
+       if( existing_tree_found == FALSE )
+               rssyl_create_default_mailbox();
+
+       prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl",
+                       _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all, NULL);
+
+       rssyl_opml_export();
+
+       if( rssyl_prefs_get()->refresh_on_startup &&
+                       claws_is_starting() )
+               g_timeout_add(2000, rssyl_refresh_all_feeds_deferred, NULL);
+}
+
+void rssyl_done(void)
+{
+       prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl",
+                       _("Refresh all feeds"));
+       rssyl_prefs_done();
+       rssyl_gtk_done();
+       if (!claws_is_exiting())
+               folder_unregister_class(rssyl_folder_get_class());
+}
+
+static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
+{
+       gchar *destfile;
+       gchar *destpath;
+
+       destpath = folder_item_get_path(dest);
+       g_return_val_if_fail(destpath != NULL, NULL);
+
+       if( !is_dir_exist(destpath) )
+               make_dir_hier(destpath);
+
+       for( ; ; ) {
+               destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
+                               dest->last_num + 1);
+               if( is_file_entry_exist(destfile) ) {
+                       dest->last_num++;
+                       g_free(destfile);
+               } else
+                       break;
+       }
+
+       g_free(destpath);
+
+       return destfile;
+}
+
+static void rssyl_get_last_num(Folder *folder, FolderItem *item)
+{
+       gchar *path;
+       DIR *dp;
+       struct dirent *d;
+       gint max = 0;
+       gint num;
+
+       g_return_if_fail(item != NULL);
+
+       debug_print("rssyl_get_last_num(): Scanning %s ...\n", item->path);
+       path = folder_item_get_path(item);
+       g_return_if_fail(path != NULL);
+       if( change_dir(path) < 0 ) {
+               g_free(path);
+               return;
+       }
+       g_free(path);
+
+       if( (dp = opendir(".")) == NULL ) {
+               FILE_OP_ERROR(item->path, "opendir");
+               return;
+       }
+
+       while( (d = readdir(dp)) != NULL ) {
+               if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
+                       if( max < num )
+                               max = num;
+               }
+       }
+       closedir(dp);
+
+       debug_print("Last number in dir %s = %d\n", item->path, max);
+       item->last_num = max;
+}
+
+struct _RSSylFolder {
+       LocalFolder folder;
+};
+
+typedef struct _RSSylFolder RSSylFolder;
+
+FolderClass rssyl_class;
+
+static Folder *rssyl_new_folder(const gchar *name, const gchar *path)
+{
+       RSSylFolder *folder;
+
+       debug_print("RSSyl: new_folder\n");
+
+       rssyl_make_rc_dir();
+
+       folder = g_new0(RSSylFolder, 1);
+       FOLDER(folder)->klass = &rssyl_class;
+       folder_init(FOLDER(folder), name);
+
+       return FOLDER(folder);
+}
+
+static void rssyl_destroy_folder(Folder *_folder)
+{
+       RSSylFolder *folder = (RSSylFolder *)_folder;
+
+       folder_local_folder_destroy(LOCAL_FOLDER(folder));
+}
+
+static gint rssyl_scan_tree(Folder *folder)
+{
+       g_return_val_if_fail(folder != NULL, -1);
+
+       folder->outbox = NULL;
+       folder->draft = NULL;
+       folder->queue = NULL;
+       folder->trash = NULL;
+
+       debug_print("RSSyl: scanning tree\n");
+       rssyl_create_tree(folder);
+
+       return 0;
+}
+
+static gint rssyl_create_tree(Folder *folder)
+{
+       FolderItem *rootitem;
+       GNode *rootnode;
+
+       rssyl_make_rc_dir();
+
+       if( !folder->node ) {
+               rootitem = folder_item_new(folder, folder->name, NULL);
+               rootitem->folder = folder;
+               rootnode = g_node_new(rootitem);
+               folder->node = rootnode;
+               rootitem->node = rootnode;
+       } else {
+               rootitem = FOLDER_ITEM(folder->node->data);
+               rootnode = folder->node;
+       }
+
+       debug_print("RSSyl: created new rssyl tree\n");
+       return 0;
+}
+
+static FolderItem *rssyl_item_new(Folder *folder)
+{
+       RSSylFolderItem *ritem;
+
+       debug_print("RSSyl: item_new\n");
+
+       ritem = g_new0(RSSylFolderItem, 1);
+
+       ritem->url = NULL;
+       ritem->default_refresh_interval = TRUE;
+       ritem->default_expired_num = TRUE;
+       ritem->fetch_comments = FALSE;
+       ritem->fetch_comments_for = -1;
+       ritem->silent_update = 0;
+       ritem->refresh_interval = rssyl_prefs_get()->refresh;
+       ritem->refresh_id = 0;
+       ritem->expired_num = rssyl_prefs_get()->expired;
+       ritem->last_count = 0;
+
+       ritem->contents = NULL;
+       ritem->feedprop = NULL;
+
+       return (FolderItem *)ritem;
+}
+
+static void rssyl_item_destroy(Folder *folder, FolderItem *item)
+{
+       RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+
+       g_return_if_fail(ritem != NULL);
+
+       /* Silently remove feed refresh timeouts */
+       if( ritem->refresh_id != 0 )
+               g_source_remove(ritem->refresh_id);
+
+       g_free(ritem->url);
+       g_free(ritem->official_name);
+       g_slist_free(ritem->contents);
+
+       g_free(item);
+}
+
+static FolderItem *rssyl_create_folder(Folder *folder,
+                                                               FolderItem *parent, const gchar *name)
+{
+       gchar *path = NULL, *tmp;
+       FolderItem *newitem = NULL;
+
+       g_return_val_if_fail(folder != NULL, NULL);
+       g_return_val_if_fail(parent != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+       tmp = rssyl_feed_title_to_dir((gchar *)name);
+       path = g_strconcat((parent->path != NULL) ? parent->path : "", ".",
+                               tmp, NULL);
+       g_free(tmp);
+       newitem = folder_item_new(folder, name, path);
+       folder_item_append(parent, newitem);
+       g_free(path);
+
+       return newitem;
+}
+
+static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
+{
+       gchar *result, *tmp;
+       tmp = rssyl_feed_title_to_dir(item->name);
+       result = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+                       G_DIR_SEPARATOR_S, tmp, NULL);
+       g_free(tmp);
+       return result;
+}
+
+static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
+                               const gchar *name)
+{
+       gchar *oldname = NULL, *oldpath = NULL, *newpath = NULL;
+       RSSylFolderItem *ritem = NULL;
+       g_return_val_if_fail(folder != NULL, -1);
+       g_return_val_if_fail(item != NULL, -1);
+       g_return_val_if_fail(item->path != NULL, -1);
+       g_return_val_if_fail(name != NULL, -1);
+
+       debug_print("RSSyl: renaming folder '%s' to '%s'\n", item->path, name);
+
+       oldpath = rssyl_item_get_path(folder, item);
+       
+       /* now get the new path using the new name */
+       oldname = item->name;
+       item->name = g_strdup(name);
+       newpath = rssyl_item_get_path(folder, item);
+       
+       /* put back the old name in case the rename fails */
+       g_free(item->name);
+       item->name = oldname;
+       
+       if (g_rename(oldpath, newpath) < 0) {
+               FILE_OP_ERROR(oldpath, "rename");
+               g_free(oldpath);
+               g_free(newpath);
+               return -1;
+       }
+       
+       g_free(item->path);
+       item->path = g_strdup_printf(".%s", name);
+       
+       ritem = (RSSylFolderItem *)item;
+       
+       if (ritem->url)
+               rssyl_props_update_name(ritem, (gchar *)name);
+       
+       g_free(item->name);
+       item->name = g_strdup(name);
+       
+       folder_write_list();
+
+       return 0;
+}
+
+static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
+{
+       g_return_val_if_fail(folder != NULL, -1);
+       g_return_val_if_fail(item != NULL, -1);
+       g_return_val_if_fail(item->path != NULL, -1);
+       g_return_val_if_fail(item->stype == F_NORMAL, -1);
+
+       debug_print("RSSyl: removing folder item %s\n", item->path);
+
+       folder_item_remove(item);
+
+       return 0;
+}
+
+static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
+               MsgNumberList **list, gboolean *old_uids_valid)
+{
+       gchar *path;
+       DIR *dp;
+       struct dirent *d;
+       gint num, nummsgs = 0;
+       RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+
+       g_return_val_if_fail(item != NULL, -1);
+
+       debug_print("RSSyl: scanning '%s'...\n", item->path);
+
+       rssyl_get_feed_props(ritem);
+
+       if (ritem->url == NULL)
+               return -1;
+
+       *old_uids_valid = TRUE;
+
+       path = folder_item_get_path(item);
+       g_return_val_if_fail(path != NULL, -1);
+       if( change_dir(path) < 0 ) {
+               g_free(path);
+               return -1;
+       }
+       g_free(path);
+
+       if( (dp = opendir(".")) == NULL ) {
+               FILE_OP_ERROR(item->path, "opendir");
+               return -1;
+       }
+
+       while( (d = readdir(dp)) != NULL ) {
+               if( (num = to_number(d->d_name)) > 0 ) {
+                       *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
+                       nummsgs++;
+               }
+       }
+       closedir(dp);
+
+       return nummsgs;
+}
+
+static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
+{
+       return TRUE;
+}
+
+static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
+{
+       gchar *snum = g_strdup_printf("%d", num);
+       gchar *tmp = rssyl_feed_title_to_dir(item->name);
+       gchar *file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
+                       G_DIR_SEPARATOR_S, tmp,
+                       G_DIR_SEPARATOR_S, snum, NULL);
+       g_free(tmp);
+       debug_print("RSSyl: fetch_msg: '%s'\n", file);
+
+       g_free(snum);
+
+       return file;
+}
+
+static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
+{
+       MsgInfo *msginfo = NULL;
+       gchar *file;
+       MsgFlags flags;
+
+       debug_print("RSSyl: get_msginfo: %d\n", num);
+
+       g_return_val_if_fail(folder != NULL, NULL);
+       g_return_val_if_fail(item != NULL, NULL);
+       g_return_val_if_fail(num > 0, NULL);
+
+       file = rssyl_fetch_msg(folder, item, num);
+       g_return_val_if_fail(file != NULL, NULL);
+
+       flags.perm_flags = MSG_NEW | MSG_UNREAD;
+       flags.tmp_flags = 0;
+
+       msginfo = rssyl_parse_feed_item_to_msginfo(file, flags, TRUE, TRUE, item);
+
+       if( msginfo )
+               msginfo->msgnum = num;
+
+       g_free(file);
+
+       return msginfo;
+}
+
+static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
+               GHashTable *relation)
+{
+       gchar *destfile;
+       GSList *cur;
+       MsgFileInfo *fileinfo;
+
+       g_return_val_if_fail(dest != NULL, -1);
+       g_return_val_if_fail(file_list != NULL, -1);
+
+       if( dest->last_num < 0 ) {
+               rssyl_get_last_num(folder, dest);
+               if( dest->last_num < 0 ) return -1;
+       }
+
+       for( cur = file_list; cur != NULL; cur = cur->next ) {
+               fileinfo = (MsgFileInfo *)cur->data;
+
+               destfile = rssyl_get_new_msg_filename(dest);
+               g_return_val_if_fail(destfile != NULL, -1);
+
+#ifdef G_OS_UNIX
+               if( link(fileinfo->file, destfile) < 0 )
+#endif
+                       if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
+                               g_warning("can't copy message %s to %s\n", fileinfo->file, destfile);
+                               g_free(destfile);
+                               return -1;
+                       }
+
+               if( relation != NULL )
+                       g_hash_table_insert(relation, fileinfo,
+                                       GINT_TO_POINTER(dest->last_num + 1) );
+               g_free(destfile);
+               dest->last_num++;
+       }
+
+       return dest->last_num;
+}
+
+static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
+               MsgFlags *flags)
+{
+       gint ret;
+       GSList file_list;
+       MsgFileInfo fileinfo;
+
+       g_return_val_if_fail(file != NULL, -1);
+
+       fileinfo.msginfo = NULL;
+       fileinfo.file = (gchar *)file;
+       fileinfo.flags = flags;
+       file_list.data = &fileinfo;
+       file_list.next = NULL;
+
+       ret = rssyl_add_msgs(folder, dest, &file_list, NULL);
+       return ret;
+}
+
+static gint rssyl_dummy_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *info)
+{
+       if (info->folder == NULL || info->folder->folder != dest->folder) {
+               return -1;
+       }
+       if (info->folder && info->folder->name && dest->name
+       &&  !strcmp(info->folder->name, dest->name)) {
+               /* this is a folder move */
+               gchar *file = procmsg_get_message_file(info);
+               gchar *tmp = g_strdup_printf("%s.tmp", file);
+               copy_file(file, tmp, TRUE);
+               g_free(file);
+               g_free(tmp);
+               return info->msgnum;
+       } else {
+               return -1;
+       }
+}
+static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
+{
+       gboolean need_scan = FALSE;
+       gchar *file, *tmp;
+
+       g_return_val_if_fail(item != NULL, -1);
+
+       file = rssyl_fetch_msg(folder, item, num);
+       g_return_val_if_fail(file != NULL, -1);
+
+       need_scan = rssyl_scan_required(folder, item);
+
+       /* are we doing a folder move ? */
+       tmp = g_strdup_printf("%s.tmp", file);
+       if (is_file_exist(tmp)) {
+               claws_unlink(tmp);
+               g_free(tmp);
+               g_free(file);
+               return 0;
+       }
+       g_free(tmp);
+       if( claws_unlink(file) < 0 ) {
+               FILE_OP_ERROR(file, "unlink");
+               g_free(file);
+               return -1;
+       }
+
+       if( !need_scan )
+               item->mtime = time(NULL);
+
+       g_free(file);
+       return 0;
+}
+
+static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
+{
+       if (folder->klass != rssyl_folder_get_class())
+               return FALSE;
+       
+       if( rssyl_subscribe_new_feed(
+                               FOLDER_ITEM(folder->node->data), uri, FALSE) != NULL )
+               return TRUE;
+       return FALSE;
+}
+
+/************************************************************************/
+
+FolderClass *rssyl_folder_get_class()
+{
+       if( rssyl_class.idstr == NULL ) {
+               rssyl_class.type = F_UNKNOWN;
+               rssyl_class.idstr = "rssyl";
+               rssyl_class.uistr = "RSSyl";
+
+               /* Folder functions */
+               rssyl_class.new_folder = rssyl_new_folder;
+               rssyl_class.destroy_folder = rssyl_destroy_folder;
+               rssyl_class.set_xml = folder_set_xml;
+               rssyl_class.get_xml = folder_get_xml;
+               rssyl_class.scan_tree = rssyl_scan_tree;
+               rssyl_class.create_tree = rssyl_create_tree;
+
+               /* FolderItem functions */
+               rssyl_class.item_new = rssyl_item_new;
+               rssyl_class.item_destroy = rssyl_item_destroy;
+               rssyl_class.item_get_path = rssyl_item_get_path;
+               rssyl_class.create_folder = rssyl_create_folder;
+               rssyl_class.rename_folder = rssyl_rename_folder;
+               rssyl_class.remove_folder = rssyl_remove_folder;
+               rssyl_class.get_num_list = rssyl_get_num_list;
+               rssyl_class.scan_required = rssyl_scan_required;
+
+               /* Message functions */
+               rssyl_class.get_msginfo = rssyl_get_msginfo;
+               rssyl_class.fetch_msg = rssyl_fetch_msg;
+               rssyl_class.copy_msg = rssyl_dummy_copy_msg;
+               rssyl_class.add_msg = rssyl_add_msg;
+               rssyl_class.add_msgs = rssyl_add_msgs;
+               rssyl_class.remove_msg = rssyl_remove_msg;
+               rssyl_class.remove_msgs = NULL;
+//             rssyl_class.change_flags = rssyl_change_flags;
+               rssyl_class.change_flags = NULL;
+               rssyl_class.subscribe = rssyl_subscribe_uri;
+               debug_print("RSSyl: registered folderclass\n");
+       }
+
+       return &rssyl_class;
+}
diff --git a/src/plugins/rssyl/rssyl.h b/src/plugins/rssyl/rssyl.h
new file mode 100644 (file)
index 0000000..541a898
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __RSSYL_H
+#define __RSSYL_H
+
+#include <glib.h>
+
+#include <folder.h>
+
+#define PLUGIN_NAME            (_("RSSyl"))
+
+/* Name of directory in rcdir where RSSyl will store its data. */
+#define RSSYL_DIR "RSSyl"
+
+/* Default RSSyl mailbox name */
+#define RSSYL_DEFAULT_MAILBOX  _("My Feeds")
+
+/* Default feed to be added when creating mailbox for the first time */
+#define RSSYL_DEFAULT_FEED     "http://planet.claws-mail.org/rss20.xml"
+
+struct _RSSylFolderItem {
+       FolderItem item;
+       GSList *contents;
+       gint last_count;
+
+       gchar *url;
+       gchar *official_name;
+
+       gboolean default_refresh_interval;
+       gint refresh_interval;
+
+       gboolean default_expired_num;
+       gint expired_num;
+
+       guint refresh_id;       
+       gboolean fetch_comments;
+       gint fetch_comments_for;
+       gint silent_update;
+
+       struct _RSSylFeedProp *feedprop;
+};
+
+typedef struct _RSSylFolderItem RSSylFolderItem;
+
+struct _RSSylRefreshCtx {
+       RSSylFolderItem *ritem;
+       guint id;
+};
+
+typedef struct _RSSylRefreshCtx RSSylRefreshCtx;
+
+void rssyl_init(void);
+void rssyl_done(void);
+
+FolderClass *rssyl_folder_get_class(void);
+
+#define IS_RSSYL_FOLDER_ITEM(item) \
+       (item->folder->klass == rssyl_folder_get_class())
+
+#endif /* __RSSYL_H */
diff --git a/src/plugins/rssyl/rssyl_cb_gtk.c b/src/plugins/rssyl/rssyl_cb_gtk.c
new file mode 100644 (file)
index 0000000..0e49bb9
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@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
new file mode 100644 (file)
index 0000000..a49d3a3
--- /dev/null
@@ -0,0 +1,19 @@
+#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
new file mode 100644 (file)
index 0000000..1e36d09
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - callback handler functions for folderview rssyl context menu items
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "folderview.h"
+#include "alertpanel.h"
+#include "gtk/inputdialog.h"
+#include "prefs_common.h"
+#include "filesel.h"
+#include "inc.h"
+#include "folder_item_prefs.h"
+
+#include "feed.h"
+#include "feedprops.h"
+#include "opml.h"
+#include "rssyl.h"
+#include "rssyl_gtk.h"
+
+void rssyl_new_feed_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+       FolderItem *item;
+       gchar *new_feed;
+
+       debug_print("RSSyl: new_feed_cb\n");
+
+       g_return_if_fail(folderview->selected != NULL);
+
+       item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       new_feed = input_dialog(_("Subscribe feed"),
+                                        _("Input the URL of the news feed you wish to subscribe:"),
+                                        "");
+       g_return_if_fail(new_feed != NULL);
+
+       rssyl_subscribe_new_feed(item, new_feed, TRUE);
+
+       g_free(new_feed);
+}
+
+void rssyl_new_folder_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+       FolderItem *item;
+       FolderItem *new_item;
+       gchar *new_folder;
+       gchar *name;
+       gchar *p;
+       RSSylFolderItem *ritem = NULL;
+
+       if (!folderview->selected) return;
+
+       item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       new_folder = input_dialog(_("New folder"),
+                                 _("Input the name of new folder:"),
+                                 _("NewFolder"));
+       if (!new_folder) return;
+       AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+
+       p = strchr(new_folder, G_DIR_SEPARATOR);
+       if (p) {
+               alertpanel_error(_("'%c' can't be included in folder name."),
+                                G_DIR_SEPARATOR);
+               return;
+       }
+
+       name = trim_string(new_folder, 32);
+       AUTORELEASE_STR(name, {g_free(name); return;});
+
+       /* find whether the directory already exists */
+       if (folder_find_child_item_by_name(item, new_folder)) {
+               alertpanel_error(_("The folder '%s' already exists."), name);
+               return;
+       }
+
+       new_item = folder_create_folder(item, new_folder);
+       if (!new_item) {
+               alertpanel_error(_("Can't create the folder '%s'."), name);
+               return;
+       }
+
+       ritem = (RSSylFolderItem *)new_item;
+       ritem->url = NULL;
+
+       folder_write_list();
+}
+
+void rssyl_remove_rss_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       FolderItem *item;
+       gchar *name, *message;
+       AlertValue aval;
+
+       debug_print("RSSyl: remove_rss_cb\n");
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+       g_return_if_fail( !folder_item_parent(item) );
+
+       name = trim_string(item->folder->name, 32);
+       message = g_strdup_printf(_("Really remove the folder tree '%s' ?\n"), name);
+       aval = alertpanel_full(_("Remove folder tree"), message,
+                        GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE, NULL,
+                         ALERT_WARNING, G_ALERTDEFAULT);
+       g_free(message);
+       g_free(name);
+
+       if (aval != G_ALERTALTERNATE)
+               return;
+
+       folderview_unselect(folderview);
+       summary_clear_all(folderview->summaryview);
+
+       folder_destroy(item->folder);
+}
+
+void rssyl_remove_feed_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+       FolderItem *item;
+       gint response;
+       GtkWidget *dialog, *rmcache_widget = NULL;
+       gboolean rmcache = FALSE;
+       debug_print("RSSyl: remove_feed_cb\n");
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->path != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       dialog = rssyl_feed_removal_dialog(item->name, &rmcache_widget);
+
+       g_return_if_fail(dialog != NULL);
+
+       gtk_widget_show_all(dialog);
+
+       response = gtk_dialog_run(GTK_DIALOG(dialog));
+
+       if( response != GTK_RESPONSE_YES ) {
+               debug_print("'No' clicked, returning\n");
+               gtk_widget_destroy(dialog);
+               return;
+       }
+
+       g_return_if_fail(rmcache_widget != NULL);
+
+       rmcache = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rmcache_widget) );
+
+       gtk_widget_destroy(dialog);
+
+       if( folderview->opened == folderview->selected ||
+                       gtk_cmctree_is_ancestor(ctree,
+                                       folderview->selected,
+                                       folderview->opened) ) {
+               summary_clear_all(folderview->summaryview);
+               folderview->opened = NULL;
+       }
+
+       rssyl_remove_feed_props((RSSylFolderItem *)item);
+
+       if( rmcache == TRUE )
+               rssyl_remove_feed_cache(item);
+
+       if( item->folder->klass->remove_folder(item->folder, item) < 0 ) {
+               gchar *tmp;
+               tmp = g_markup_printf_escaped(_("Can't remove feed '%s'."), item->name);
+               alertpanel_error("%s", tmp);
+               g_free(tmp);
+               if( folderview->opened == folderview->selected )
+                       summary_show(folderview->summaryview,
+                                       folderview->summaryview->folder_item);
+               return;
+       }
+
+       folder_write_list();
+}
+
+void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+       FolderItem *item;
+       gchar *message, *name;
+       AlertValue avalue;
+       gchar *old_path;
+       gchar *old_id;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->path != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       name = trim_string(item->name, 32);
+       AUTORELEASE_STR(name, {g_free(name); return;});
+       message = g_strdup_printf
+               (_("All folders and messages under '%s' will be permanently deleted. "
+                  "Recovery will not be possible.\n\n"
+                  "Do you really want to delete?"), name);
+       avalue = alertpanel_full(_("Delete folder"), message,
+                                GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, FALSE,
+                                NULL, ALERT_WARNING, G_ALERTDEFAULT);
+       g_free(message);
+       if (avalue != G_ALERTALTERNATE) return;
+
+       Xstrdup_a(old_path, item->path, return);
+       old_id = folder_item_get_identifier(item);
+
+       if (folderview->opened == folderview->selected ||
+           gtk_cmctree_is_ancestor(ctree,
+                                 folderview->selected,
+                                 folderview->opened)) {
+               summary_clear_all(folderview->summaryview);
+               folderview->opened = NULL;
+       }
+
+       if (item->folder->klass->remove_folder(item->folder, item) < 0) {
+               folder_item_scan(item);
+               alertpanel_error(_("Can't remove the folder '%s'."), name);
+               g_free(old_id);
+               return;
+       }
+
+       folder_write_list();
+
+       prefs_filtering_delete_path(old_id);
+       g_free(old_id);
+
+}
+
+void rssyl_refresh_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       FolderItem *item;
+       RSSylFolderItem *ritem;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       ritem = (RSSylFolderItem *)item;
+
+       if (prefs_common_get_prefs()->work_offline && 
+          !inc_offline_should_override(TRUE,
+                       ngettext(
+                          "Claws Mail needs network access in order "
+                          "to update the feed.",
+                          "Claws Mail needs network access in order "
+                          "to update the feeds.", 1))) {
+                       return;
+       }
+
+       main_window_cursor_wait(mainwindow_get_mainwindow());
+       rssyl_update_feed(ritem);
+       main_window_cursor_normal(mainwindow_get_mainwindow());
+}
+
+void rssyl_refresh_all_cb(GtkAction *action, gpointer data)
+{
+       main_window_cursor_wait(mainwindow_get_mainwindow());
+       rssyl_refresh_all_feeds();
+       main_window_cursor_normal(mainwindow_get_mainwindow());
+}
+
+void rssyl_prop_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       FolderItem *item;
+       RSSylFolderItem *ritem;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       debug_print("RSSyl: rssyl_prop_cb() for '%s'\n", item->name);
+
+       ritem = (RSSylFolderItem *)item;
+       rssyl_get_feed_props(ritem);
+       rssyl_gtk_prop(ritem);
+}
+
+void rssyl_rename_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       FolderItem *item;
+       gchar *new_folder;
+       gchar *name;
+       gchar *message;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->path != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       name = trim_string(item->name, 32);
+       message = g_strdup_printf(_("Input new name for '%s':"), name);
+       new_folder = input_dialog(_("Rename folder"), message, item->name);
+       g_free(message);
+       g_free(name);
+       if (!new_folder) return;
+       AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+
+       if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
+               alertpanel_error(_("'%c' can't be included in folder name."),
+                                G_DIR_SEPARATOR);
+               return;
+       }
+
+       if (folder_find_child_item_by_name(folder_item_parent(item), new_folder)) {
+               name = trim_string(new_folder, 32);
+               alertpanel_error(_("The folder '%s' already exists."), name);
+               g_free(name);
+               return;
+       }
+
+       if (folder_item_rename(item, new_folder) < 0) {
+               alertpanel_error(_("The folder could not be renamed.\n"
+                                  "The new folder name is not allowed."));
+               return;
+       }
+
+       folder_item_prefs_save_config_recursive(item);
+       folder_write_list();
+}
+
+void rssyl_import_feed_list_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       debug_print("RSSyl: rssyl_import_feed_cb\n");
+
+       FolderItem *item;
+       gchar *opmlfile = NULL;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       opmlfile = filesel_select_file_open_with_filter(
+                       _("Select a .opml file"), NULL, "*.opml");
+       
+       if (!is_file_exist(opmlfile)) {
+               g_free(opmlfile);
+               return;
+       }
+
+       rssyl_opml_import(opmlfile, item);
+}
diff --git a/src/plugins/rssyl/rssyl_cb_menu.h b/src/plugins/rssyl/rssyl_cb_menu.h
new file mode 100644 (file)
index 0000000..098f43c
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __RSSYL_MENU_CB
+#define __RSSYL_MENU_CB
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "folderview.h"
+
+void rssyl_new_feed_cb(GtkAction *action, gpointer data);
+void rssyl_remove_feed_cb(GtkAction *action, gpointer data);
+void rssyl_remove_rss_cb(GtkAction *action, gpointer data);
+void rssyl_refresh_cb(GtkAction *action, gpointer data);
+void rssyl_prop_cb(GtkAction *action, gpointer data);
+void rssyl_import_feed_list_cb(GtkAction *action, gpointer data);
+void rssyl_refresh_all_cb(GtkAction *action, gpointer data);
+void rssyl_rename_cb(GtkAction *action, gpointer data);
+void rssyl_new_folder_cb(GtkAction *action, gpointer data);
+void rssyl_remove_folder_cb(GtkAction *action, gpointer data);
+
+#endif /* __RSSYL_MENU_FEED */
diff --git a/src/plugins/rssyl/rssyl_gtk.c b/src/plugins/rssyl/rssyl_gtk.c
new file mode 100644 (file)
index 0000000..fec09e6
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - GUI handling functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "gtk/menu.h"
+#include "mainwindow.h"
+#include "inputdialog.h"
+#include "folderview.h"
+#include "alertpanel.h"
+#include "summaryview.h"
+
+#include "main.h"
+#include "gtkutils.h"
+
+#include "feed.h"
+#include "feedprops.h"
+#include "rssyl.h"
+#include "rssyl_cb_gtk.h"
+#include "rssyl_cb_menu.h"
+#include "rssyl_gtk.h"
+#include "rssyl_prefs.h"
+
+static char *rssyl_popup_menu_labels[] =
+{
+       N_("_Refresh feed"),
+       N_("Refresh _all feeds"),
+       N_("Subscribe _new feed..."),
+       N_("_Unsubscribe feed..."),
+       N_("Feed pr_operties..."),
+       N_("Import feed list..."),
+       N_("Rena_me..."),
+       N_("_Create new folder..."),
+       N_("_Delete folder..."),
+       N_("Remove folder _tree..."),
+       NULL
+};
+
+static GtkActionEntry rssyl_popup_entries[] = 
+{
+       {"FolderViewPopup/RefreshFeed",         NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_refresh_cb) },
+       {"FolderViewPopup/RefreshAllFeeds",     NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_refresh_all_cb) },
+
+       {"FolderViewPopup/NewFeed",             NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_feed_cb) },
+       {"FolderViewPopup/RemoveFeed",          NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_feed_cb) },
+       {"FolderViewPopup/FeedProperties",      NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_prop_cb) },
+       {"FolderViewPopup/ImportFeedlist",      NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_import_feed_list_cb) },
+
+       {"FolderViewPopup/RenameFolder",        NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_rename_cb) },
+
+       {"FolderViewPopup/NewFolder",           NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_new_folder_cb) },
+       {"FolderViewPopup/RemoveFolder",        NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_folder_cb) },
+
+       {"FolderViewPopup/RemoveMailbox",       NULL, NULL, NULL, NULL, G_CALLBACK(rssyl_remove_rss_cb) },
+
+};
+
+static void rssyl_add_menuitems(GtkUIManager *ui_manager, FolderItem *item)
+{
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RefreshFeed", "FolderViewPopup/RefreshFeed", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RefreshAllFeeds", "FolderViewPopup/RefreshAllFeeds", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "NewFeed", "FolderViewPopup/NewFeed", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveFeed", "FolderViewPopup/RemoveFeed", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "FeedProperties", "FolderViewPopup/FeedProperties", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "ImportFeedlist", "FolderViewPopup/ImportFeedlist", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS2", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RenameFolder", "FolderViewPopup/RenameFolder", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "NewFolder", "FolderViewPopup/NewFolder", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveFolder", "FolderViewPopup/RemoveFolder", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS4", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveMailbox", "FolderViewPopup/RemoveMailbox", GTK_UI_MANAGER_MENUITEM)
+       MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorRSS5", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+}
+
+static void rssyl_set_sensitivity(GtkUIManager *ui_manager, FolderItem *item)
+{
+#define SET_SENS(name, sens) \
+       cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
+
+       RSSylFolderItem *ritem = (RSSylFolderItem *)item;
+       SET_SENS("FolderViewPopup/RefreshFeed", folder_item_parent(item) != NULL && ritem->url);
+       SET_SENS("FolderViewPopup/RefreshAllFeeds", folder_item_parent(item) == NULL );
+       SET_SENS("FolderViewPopup/NewFeed", TRUE);
+       SET_SENS("FolderViewPopup/ImportFeedlist", TRUE );
+       SET_SENS("FolderViewPopup/RemoveFeed", folder_item_parent(item) != NULL && ritem->url );
+       SET_SENS("FolderViewPopup/FeedProperties", folder_item_parent(item) != NULL && ritem->url );
+       SET_SENS("FolderViewPopup/RenameFolder", folder_item_parent(item) != NULL );
+       SET_SENS("FolderViewPopup/NewFolder", TRUE );
+       SET_SENS("FolderViewPopup/RemoveFolder", folder_item_parent(item) != NULL && !ritem->url );
+       SET_SENS("FolderViewPopup/RemoveMailbox", folder_item_parent(item) == NULL );
+
+#undef SET_SENS
+}
+
+static FolderViewPopup rssyl_popup =
+{
+       "rssyl",
+       "<rssyl>",
+       rssyl_popup_entries,
+       G_N_ELEMENTS(rssyl_popup_entries),
+       NULL, 0,
+       NULL, 0, 0, NULL,
+       rssyl_add_menuitems,
+       rssyl_set_sensitivity
+};
+
+static void rssyl_fill_popup_menu_labels(void)
+{
+       gint i;
+
+       for( i = 0; rssyl_popup_menu_labels[i] != NULL; i++ ) {
+               (rssyl_popup_entries[i]).label = _(rssyl_popup_menu_labels[i]);
+       }
+}
+
+static void rssyl_add_mailbox(GtkAction *action, gpointer callback_data)
+{
+       MainWindow *mainwin = (MainWindow *) callback_data;
+       gchar *path;
+       gchar *base = NULL;
+       Folder *folder;
+
+       path = input_dialog(_("Add RSS folder tree"),
+                       _("Enter name for a new RSS folder tree."),
+                       RSSYL_DEFAULT_MAILBOX);
+       if( !path ) return;
+
+       if( folder_find_from_path(path) ) {
+               alertpanel_error(_("The mailbox '%s' already exists."), path);
+               g_free(path);
+               return;
+       }
+
+       base = g_path_get_basename(path);
+       folder = folder_new(folder_get_class_from_string("rssyl"),
+                       base, path);
+       g_free(base);
+
+       if( folder->klass->create_tree(folder) < 0 ) {
+               alertpanel_error(_("Creation of folder tree failed.\n"
+                               "Maybe some files already exist, or you don't have the permission "
+                               "to write there?"));
+               folder_destroy(folder);
+               return;
+       }
+
+       folder_add(folder);
+       folder_scan_tree(folder, TRUE);
+
+       folderview_set(mainwin->folderview);
+}
+
+static GtkActionEntry mainwindow_add_mailbox[] = {{
+       "File/AddMailbox/RSSyl",
+       NULL, N_("RSSyl..."), NULL, NULL, G_CALLBACK(rssyl_add_mailbox)
+}};
+
+static guint main_menu_id = 0;
+
+void rssyl_gtk_init(void)
+{
+       MainWindow *mainwin = mainwindow_get_mainwindow();
+
+       gtk_action_group_add_actions(mainwin->action_group, mainwindow_add_mailbox,
+                       1, (gpointer)mainwin);
+       MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/File/AddMailbox", "RSSyl", 
+                         "File/AddMailbox/RSSyl", GTK_UI_MANAGER_MENUITEM,
+                         main_menu_id)
+
+
+       rssyl_fill_popup_menu_labels();
+       folderview_register_popup(&rssyl_popup);
+}
+
+void rssyl_gtk_done(void)
+{
+       MainWindow *mainwin = mainwindow_get_mainwindow();
+       FolderView *folderview = NULL;
+       FolderItem *fitem = NULL;
+
+       if (mainwin == NULL || claws_is_exiting())
+               return;
+
+       folderview = mainwin->folderview;
+       fitem = folderview->summaryview->folder_item;
+
+       if( fitem && IS_RSSYL_FOLDER_ITEM(fitem) ) {
+               folderview_unselect(folderview);
+               summary_clear_all(folderview->summaryview);
+       }
+
+       folderview_unregister_popup(&rssyl_popup);
+
+       MENUITEM_REMUI_MANAGER(mainwin->ui_manager,mainwin->action_group, "File/AddMailbox/RSSyl", main_menu_id);
+       main_menu_id = 0;
+}
+
+/***********************************************/
+
+static RSSylFeedProp *rssyl_gtk_prop_real(RSSylFolderItem *ritem)
+{
+       MainWindow *mainwin = mainwindow_get_mainwindow();
+       RSSylFeedProp *feedprop;
+       GtkWidget *vbox, *urllabel, *urlframe, *urlalign, *table, *refresh_label,
+                                               *expired_label, *hsep, *sep, *bbox, *cancel_button, *cancel_align,
+                                               *cancel_hbox, *cancel_image, *cancel_label, *ok_button, *ok_align,
+                                               *ok_hbox, *ok_image, *ok_label, *silent_update_label;
+       GtkObject *refresh_adj, *expired_adj, *fetch_comments_for_adj;
+       gint refresh, expired;
+       gint row = 0;
+
+       g_return_val_if_fail(ritem != NULL, NULL);
+
+       feedprop = g_new0(RSSylFeedProp, 1);
+
+       /* Create required widgets */
+
+       /* Window */
+       feedprop->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "rssyl_gtk");
+
+       /* URL entry */
+       feedprop->url = gtk_entry_new();
+       gtk_entry_set_text(GTK_ENTRY(feedprop->url), ritem->url);
+
+       /* "Use default refresh interval" checkbutton */
+       feedprop->default_refresh_interval = gtk_check_button_new_with_mnemonic(
+                       _("Use default refresh interval"));
+       gtk_toggle_button_set_active(
+                       GTK_TOGGLE_BUTTON(feedprop->default_refresh_interval),
+                       ritem->default_refresh_interval);
+
+       if( ritem->refresh_interval >= 0 && !ritem->default_refresh_interval )
+               refresh = ritem->refresh_interval;
+       else
+               refresh = rssyl_prefs_get()->refresh;
+
+       /* "Keep default number of expired items" checkbutton */
+       feedprop->default_expired_num = gtk_check_button_new_with_mnemonic(
+                       _("Keep default number of expired entries"));
+       gtk_toggle_button_set_active(
+                       GTK_TOGGLE_BUTTON(feedprop->default_expired_num),
+                       ritem->default_expired_num);
+
+       feedprop->fetch_comments = gtk_check_button_new_with_mnemonic(
+                       _("Fetch comments if possible"));
+       gtk_toggle_button_set_active(
+                       GTK_TOGGLE_BUTTON(feedprop->fetch_comments),
+                       ritem->fetch_comments);
+
+       /* Refresh interval spinbutton */
+       fetch_comments_for_adj = gtk_adjustment_new(ritem->fetch_comments_for,
+                       -1, 100000, 1, 10, 0);
+       feedprop->fetch_comments_for = gtk_spin_button_new(GTK_ADJUSTMENT(fetch_comments_for_adj),
+                       1, 0);
+
+       if( ritem->default_expired_num )
+               expired = rssyl_prefs_get()->expired;
+       else
+               expired = ritem->expired_num;
+
+       /* Refresh interval spinbutton */
+       refresh_adj = gtk_adjustment_new(refresh,
+                       0, 100000, 1, 10, 0);
+       feedprop->refresh_interval = gtk_spin_button_new(GTK_ADJUSTMENT(refresh_adj),
+                       1, 0);
+
+       /* Expired num spinbutton */
+       expired_adj = gtk_adjustment_new(ritem->expired_num, -1, 100000, 1, 10, 0);
+       feedprop->expired_num = gtk_spin_button_new(GTK_ADJUSTMENT(expired_adj),
+                       1, 0);
+
+       vbox = gtk_vbox_new(FALSE, 0);
+       gtk_container_add(GTK_CONTAINER(feedprop->window), vbox);
+
+       /* URL frame */
+       urlframe = gtk_frame_new(NULL);
+       gtk_container_set_border_width(GTK_CONTAINER(urlframe), 5);
+       gtk_frame_set_label_align(GTK_FRAME(urlframe), 0.05, 0.5);
+       gtk_frame_set_shadow_type(GTK_FRAME(urlframe), GTK_SHADOW_ETCHED_OUT);
+       gtk_box_pack_start(GTK_BOX(vbox), urlframe, FALSE, FALSE, 0);
+
+       /* Label for URL frame */
+       urllabel = gtk_label_new(_("<b>Source URL:</b>"));
+       gtk_label_set_use_markup(GTK_LABEL(urllabel), TRUE);
+       gtk_misc_set_padding(GTK_MISC(urllabel), 5, 0);
+       gtk_frame_set_label_widget(GTK_FRAME(urlframe), urllabel);
+
+       /* URL entry (+ alignment in frame) */
+       urlalign = gtk_alignment_new(0, 0.5, 1, 1);
+       gtk_alignment_set_padding(GTK_ALIGNMENT(urlalign), 5, 5, 5, 5);
+       gtk_container_add(GTK_CONTAINER(urlframe), urlalign);
+
+       gtk_entry_set_activates_default(GTK_ENTRY(feedprop->url), TRUE);
+       gtk_container_add(GTK_CONTAINER(urlalign), feedprop->url);
+
+       /* Table for remaining properties */
+       table = gtk_table_new(8, 2, FALSE);
+       gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
+
+       /* Fetch comments - checkbutton */
+       gtk_table_attach(GTK_TABLE(table), feedprop->fetch_comments,
+                       0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 0);
+       g_signal_connect(G_OBJECT(feedprop->fetch_comments), "toggled",
+                       G_CALLBACK(rssyl_fetch_comments_toggled_cb),
+                       (gpointer)feedprop->fetch_comments_for);
+       row++;
+
+       /* Fetch comments for - label */
+       refresh_label = gtk_label_new(_("<b>Fetch comments on posts aged less than:</b>\n"
+                       "<small>(In days; set to -1 to fetch all comments)"
+                       "</small>"));
+       gtk_label_set_use_markup(GTK_LABEL(refresh_label), TRUE);
+       gtk_misc_set_alignment(GTK_MISC(refresh_label), 0, 0.5);
+       gtk_table_attach(GTK_TABLE(table), refresh_label, 0, 1, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       /* Fetch comments for - spinbutton */
+       gtk_widget_set_sensitive(feedprop->fetch_comments_for,
+                       ritem->fetch_comments);
+       gtk_table_attach(GTK_TABLE(table), feedprop->fetch_comments_for, 1, 2, row, row+1,
+                       (GtkAttachOptions) (0),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       row++;
+       hsep = gtk_hseparator_new();
+       gtk_widget_set_size_request(hsep, -1, 10);
+       gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       row++;
+       /* Use default refresh interval - checkbutton */
+       gtk_table_attach(GTK_TABLE(table), feedprop->default_refresh_interval,
+                       0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 0);
+       g_signal_connect(G_OBJECT(feedprop->default_refresh_interval), "toggled",
+                       G_CALLBACK(rssyl_default_refresh_interval_toggled_cb),
+                       (gpointer)feedprop->refresh_interval);
+       row++;
+       /* Refresh interval - label */
+       refresh_label = gtk_label_new(_("<b>Refresh interval in minutes:</b>\n"
+                       "<small>(Set to 0 to disable automatic refreshing for this feed)"
+                       "</small>"));
+       gtk_label_set_use_markup(GTK_LABEL(refresh_label), TRUE);
+       gtk_misc_set_alignment(GTK_MISC(refresh_label), 0, 0.5);
+       gtk_table_attach(GTK_TABLE(table), refresh_label, 0, 1, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       /* Refresh interval - spinbutton */
+       gtk_widget_set_sensitive(feedprop->refresh_interval,
+                       !ritem->default_refresh_interval);
+       gtk_table_attach(GTK_TABLE(table), feedprop->refresh_interval, 1, 2, row, row+1,
+                       (GtkAttachOptions) (0),
+                       (GtkAttachOptions) (0), 10, 5);
+       row++;
+       hsep = gtk_hseparator_new();
+       gtk_widget_set_size_request(hsep, -1, 10);
+       gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       row++;
+       /* Use default number for expired - checkbutton */
+       gtk_table_attach(GTK_TABLE(table), feedprop->default_expired_num,       0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 0);
+       g_signal_connect(G_OBJECT(feedprop->default_expired_num), "toggled",
+                       G_CALLBACK(rssyl_default_expired_num_toggled_cb),
+                       (gpointer)feedprop->expired_num);
+
+       row++;
+       /* Expired items - label */
+       expired_label = gtk_label_new(_("<b>Number of expired entries to keep:"
+                       "</b>\n<small>(Set to -1 if you want to keep expired entries)"
+                       "</small>"));
+       gtk_label_set_use_markup(GTK_LABEL(expired_label), TRUE);
+       gtk_misc_set_alignment(GTK_MISC(expired_label), 0, 0.5);
+       gtk_table_attach(GTK_TABLE(table), expired_label, 0, 1, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       /* Expired items - spinbutton */
+       gtk_widget_set_sensitive(feedprop->expired_num,
+                       !ritem->default_expired_num);
+       gtk_table_attach(GTK_TABLE(table), feedprop->expired_num, 1, 2, row, row+1,
+                       (GtkAttachOptions) (0),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       row++;
+       hsep = gtk_hseparator_new();
+       gtk_widget_set_size_request(hsep, -1, 10);
+       gtk_table_attach(GTK_TABLE(table), hsep, 0, 2, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       row++;
+       /* Silent update - label */
+       silent_update_label =
+               gtk_label_new(_("<b>If an item changes, do not mark it as unread:</b>"));
+       gtk_label_set_use_markup(GTK_LABEL(silent_update_label), TRUE);
+       gtk_misc_set_alignment(GTK_MISC(silent_update_label), 0, 0.5);
+       gtk_table_attach(GTK_TABLE(table), silent_update_label, 0, 1, row, row+1,
+                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                       (GtkAttachOptions) (0), 10, 5);
+
+       /* Silent update - combobox */
+       feedprop->silent_update = gtk_combo_box_new_text();
+       gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
+                       _("Always mark as unread"));
+       gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
+                       _("If only its text changed"));
+       gtk_combo_box_append_text(GTK_COMBO_BOX(feedprop->silent_update),
+                       _("Never mark as unread"));
+       gtk_table_attach(GTK_TABLE(table), feedprop->silent_update, 1, 2, row, row+1,
+                       (GtkAttachOptions) (0),
+                       (GtkAttachOptions) (0), 10, 5);
+       gtk_combo_box_set_active(GTK_COMBO_BOX(feedprop->silent_update),
+                       ritem->silent_update);
+
+       /* Separator above the button box */
+       sep = gtk_hseparator_new();
+       gtk_widget_set_size_request(sep, -1, 10);
+       gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+       /* Buttonbox */
+       bbox = gtk_hbutton_box_new();
+       gtk_container_set_border_width(GTK_CONTAINER(bbox), 5);
+       gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+       gtk_box_set_spacing(GTK_BOX(bbox), 5);
+       gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+       /* Cancel button */
+       cancel_button = gtk_button_new();
+       gtk_container_add(GTK_CONTAINER(bbox), cancel_button);
+
+       cancel_align = gtk_alignment_new(0.5, 0.5, 0, 0);
+       gtk_container_add(GTK_CONTAINER(cancel_button), cancel_align);
+
+       cancel_hbox = gtk_hbox_new(FALSE, 2);
+       gtk_container_add(GTK_CONTAINER(cancel_align), cancel_hbox);
+
+       cancel_image = gtk_image_new_from_stock(GTK_STOCK_CANCEL,
+                       GTK_ICON_SIZE_BUTTON);
+       gtk_box_pack_start(GTK_BOX(cancel_hbox), cancel_image, FALSE, FALSE, 0);
+
+       cancel_label = gtk_label_new_with_mnemonic(_("_Cancel"));
+       gtk_box_pack_end(GTK_BOX(cancel_hbox), cancel_label, FALSE, FALSE, 0);
+
+       g_signal_connect(G_OBJECT(cancel_button), "clicked",
+                       G_CALLBACK(rssyl_props_cancel_cb), ritem);
+
+       /* OK button */
+       ok_button = gtk_button_new();
+       gtk_container_add(GTK_CONTAINER(bbox), ok_button);
+       gtkut_widget_set_can_default(ok_button, TRUE);
+
+       ok_align = gtk_alignment_new(0.5, 0.5, 0, 0);
+       gtk_container_add(GTK_CONTAINER(ok_button), ok_align);
+
+       ok_hbox = gtk_hbox_new(FALSE, 2);
+       gtk_container_add(GTK_CONTAINER(ok_align), ok_hbox);
+
+       ok_image = gtk_image_new_from_stock(GTK_STOCK_OK,
+                       GTK_ICON_SIZE_BUTTON);
+       gtk_box_pack_start(GTK_BOX(ok_hbox), ok_image, FALSE, FALSE, 0);
+
+       ok_label = gtk_label_new_with_mnemonic(_("_OK"));
+       gtk_box_pack_end(GTK_BOX(ok_hbox), ok_label, FALSE, FALSE, 0);
+
+       g_signal_connect(G_OBJECT(ok_button), "clicked",
+                       G_CALLBACK(rssyl_props_ok_cb), ritem);
+
+       /* Set some misc. stuff */
+       gtk_window_set_title(GTK_WINDOW(feedprop->window),
+                       g_strdup(_("Set feed properties")) );
+       gtk_window_set_modal(GTK_WINDOW(feedprop->window), TRUE);
+       gtk_window_set_transient_for(GTK_WINDOW(feedprop->window),
+                       GTK_WINDOW(mainwin->window) );
+
+       /* Attach callbacks to handle Enter and Escape keys */
+       g_signal_connect(G_OBJECT(feedprop->window), "key_press_event",
+                       G_CALLBACK(rssyl_props_key_press_cb), ritem);
+
+       /* ...and voila! */
+       gtk_widget_show_all(feedprop->window);
+       gtk_widget_grab_default(ok_button);
+
+       /* Unselect the text in URL entry */
+       gtk_editable_select_region(GTK_EDITABLE(feedprop->url), 0, 0);
+
+       ritem->feedprop = feedprop;
+
+       return feedprop;
+}
+
+void rssyl_gtk_prop(RSSylFolderItem *ritem)
+{
+       g_return_if_fail(ritem != NULL);
+
+       rssyl_gtk_prop_real(ritem);
+}
+
+void rssyl_gtk_prop_store(RSSylFolderItem *ritem)
+{
+       gchar *url;
+       gint x, old_ri, old_ex, old_fetch_comments;
+       gboolean use_default_ri = FALSE, use_default_ex = FALSE;
+
+       g_return_if_fail(ritem != NULL);
+       g_return_if_fail(ritem->feedprop != NULL);
+
+       url = (gchar *)gtk_entry_get_text(GTK_ENTRY(ritem->feedprop->url));
+
+       if( strlen(url) ) {
+               if( ritem->url ) {
+                       g_free(ritem->url);
+               }
+               ritem->url = g_strdup(url);
+       }
+
+       use_default_ri = gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON(ritem->feedprop->default_refresh_interval));
+       ritem->default_refresh_interval = use_default_ri;
+       debug_print("store: default is %s\n", ( use_default_ri ? "ON" : "OFF" ) );
+
+       /* Use default if checkbutton is set */
+       if( use_default_ri )
+               x = rssyl_prefs_get()->refresh;
+       else
+               x = gtk_spin_button_get_value_as_int(
+                               GTK_SPIN_BUTTON(ritem->feedprop->refresh_interval) );
+
+       old_ri = ritem->refresh_interval;
+       ritem->refresh_interval = x;
+
+       /* Update refresh interval setting if it has changed and has a sane value */
+       if( old_ri != x && x >= 0 ) {
+               debug_print("RSSyl: GTK - refresh interval changed to %d , updating "
+                               "timeout\n", ritem->refresh_interval);
+               /* Value of 0 means we do not want to update automatically */
+               if( x > 0 )
+                       rssyl_start_refresh_timeout(ritem);
+       }
+
+       old_fetch_comments = ritem->fetch_comments;
+       ritem->fetch_comments = gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON(ritem->feedprop->fetch_comments));
+
+       ritem->fetch_comments_for = gtk_spin_button_get_value_as_int(
+                               GTK_SPIN_BUTTON(ritem->feedprop->fetch_comments_for));
+
+       if (!old_fetch_comments && ritem->fetch_comments) {
+               /* reset the RSSylFolderItem's mtime to be sure we get all 
+                * available comments */
+                ritem->item.mtime = 0;
+       }
+       
+       use_default_ex = gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON(ritem->feedprop->default_expired_num));
+       ritem->default_expired_num = use_default_ex;
+       debug_print("store: default is %s\n", ( use_default_ex ? "ON" : "OFF" ) );
+
+       /* Use default if checkbutton is set */
+       if( use_default_ex )
+               x = rssyl_prefs_get()->expired;
+       else
+               x = gtk_spin_button_get_value_as_int(
+                               GTK_SPIN_BUTTON(ritem->feedprop->expired_num) );
+
+       old_ex = ritem->expired_num;
+       ritem->expired_num = x;
+
+       ritem->silent_update =
+               gtk_combo_box_get_active(GTK_COMBO_BOX(ritem->feedprop->silent_update));
+       if( ritem->silent_update < 0 )
+               ritem->silent_update = 0;
+
+       rssyl_store_feed_props(ritem);
+
+       debug_print("last_count %d, x %d, old_ex %d\n", ritem->last_count, x, old_ex);
+
+       /* update if new setting is lower, or if it was changed from -1 */
+       if( ritem->last_count != 0 && x != -1 && (old_ex > x || old_ex == -1) ) {
+               debug_print("RSSyl: GTK - expired_num has changed to %d, expiring\n",
+                               ritem->expired_num);
+               rssyl_expire_items(ritem);
+       }
+}
+
+GtkWidget *rssyl_feed_removal_dialog(gchar *name, GtkWidget **rmcache_widget)
+{
+       gchar *message;
+       GtkWidget *dialog, *vbox, *hbox, *image, *vbox2, *label, *cb, *aa,
+                                               *bno, *byes;
+       MainWindow *mainwin = mainwindow_get_mainwindow();
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       dialog = gtk_dialog_new();
+       gtk_window_set_title(GTK_WINDOW(dialog), _("Unsubscribe feed"));
+       gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+       vbox = GTK_DIALOG(dialog)->vbox;
+
+       hbox = gtk_hbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+
+       /* Question icon */
+       image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
+                       GTK_ICON_SIZE_DIALOG);
+       gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.30);
+       gtk_misc_set_padding(GTK_MISC(image), 12, 0);
+       gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
+
+       vbox2 = gtk_vbox_new(FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
+
+       /* Dialog text label */
+       label = gtk_label_new("");
+       gtk_misc_set_alignment(GTK_MISC(label), 0.1, 0);
+       gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+       gtk_misc_set_padding(GTK_MISC(label), 0, 12);
+       message = g_markup_printf_escaped("<span size='x-large'><b>%s</b></span>"
+                       "\n\n%s '%s' ?", _("Unsubscribe feed"),
+                       _("Do you really want to remove feed"), name);
+       gtk_label_set_markup(GTK_LABEL(label), message);
+       g_free(message);
+       gtk_box_pack_start(GTK_BOX(vbox2), label, FALSE, FALSE, 0);
+
+       /* Remove cache checkbutton */
+       cb = gtk_check_button_new_with_mnemonic(_("Remove cached entries"));
+       gtk_button_set_focus_on_click(GTK_BUTTON(cb), FALSE);
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb), TRUE);
+       gtk_box_pack_start(GTK_BOX(vbox2), cb, FALSE, FALSE, 0);
+       *rmcache_widget = cb;
+
+       aa = GTK_DIALOG(dialog)->action_area;
+       gtk_button_box_set_layout(GTK_BUTTON_BOX(aa), GTK_BUTTONBOX_END);
+
+       bno = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+       gtk_dialog_add_action_widget(GTK_DIALOG(dialog), bno, GTK_RESPONSE_NO);
+       gtkut_widget_set_can_default(bno, TRUE);
+       
+       byes = gtk_button_new_with_mnemonic(_("_Unsubscribe"));
+       gtk_dialog_add_action_widget(GTK_DIALOG(dialog), byes, GTK_RESPONSE_YES);
+
+       gtk_widget_grab_default(bno);
+       gtk_window_set_transient_for(GTK_WINDOW(dialog),
+                       GTK_WINDOW(mainwin->window) );
+
+       return dialog;
+}
diff --git a/src/plugins/rssyl/rssyl_gtk.h b/src/plugins/rssyl/rssyl_gtk.h
new file mode 100644 (file)
index 0000000..fdf27b3
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __RSSYL_GTK
+#define __RSSYL_GTK
+
+#include <gtk/gtk.h>
+
+#include <folder.h>
+
+#include "rssyl.h"
+
+struct _RSSylFeedProp {
+       GtkWidget *window;
+       GtkWidget *url;
+       GtkWidget *default_refresh_interval;
+       GtkWidget *refresh_interval;
+       GtkWidget *default_expired_num;
+       GtkWidget *expired_num;
+       GtkWidget *fetch_comments;
+       GtkWidget *fetch_comments_for;
+       GtkWidget *silent_update;
+};
+
+typedef struct _RSSylFeedProp RSSylFeedProp;
+
+void rssyl_gtk_init(void);
+void rssyl_gtk_done(void);
+
+void rssyl_gtk_prop(RSSylFolderItem *ritem);
+void rssyl_gtk_prop_store(RSSylFolderItem *ritem);
+
+GtkWidget *rssyl_feed_removal_dialog(gchar *name, GtkWidget **rmcache_widget);
+
+#endif /* __RSSYL_GTK */
diff --git a/src/plugins/rssyl/rssyl_prefs.c b/src/plugins/rssyl/rssyl_prefs.c
new file mode 100644 (file)
index 0000000..ea8e73f
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto
+ * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
+ *
+ * - Plugin preferences
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "common/defs.h"
+#include "common/utils.h"
+#include "gtk/gtkutils.h"
+#include "prefs_gtk.h"
+
+#include "rssyl_prefs.h"
+
+static RSSylPrefsPage rssyl_prefs_page;
+RSSylPrefs rssyl_prefs;
+
+static void destroy_rssyl_prefs_page(PrefsPage *page);
+static void create_rssyl_prefs_page(PrefsPage *page,
+               GtkWindow *window, gpointer data);
+static void save_rssyl_prefs(PrefsPage *page);
+
+static PrefParam param[] = {
+       { "refresh_interval", RSSYL_PREF_DEFAULT_REFRESH, &rssyl_prefs.refresh, P_INT,
+               NULL, NULL, NULL },
+       { "expired_keep", RSSYL_PREF_DEFAULT_EXPIRED, &rssyl_prefs.expired, P_INT,
+               NULL, NULL, NULL },
+       { "refresh_on_startup", FALSE, &rssyl_prefs.refresh_on_startup, P_BOOL,
+               NULL, NULL, NULL },
+       { "cookies_path", "", &rssyl_prefs.cookies_path, P_STRING,
+               NULL, NULL, NULL },
+       { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+void rssyl_prefs_init(void)
+{
+       static gchar *path[3];
+       gchar *rcpath;
+
+       path[0] = _("Plugins");
+       path[1] = "RSSyl";              /* We don't need this translated */
+       path[2] = NULL;
+
+       prefs_set_default(param);
+       rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+       prefs_read_config(param, PREFS_BLOCK_NAME, rcpath, NULL);
+       g_free(rcpath);
+
+       rssyl_prefs_page.page.path = path;
+       rssyl_prefs_page.page.create_widget = create_rssyl_prefs_page;
+       rssyl_prefs_page.page.destroy_widget = destroy_rssyl_prefs_page;
+       rssyl_prefs_page.page.save_page = save_rssyl_prefs;
+       rssyl_prefs_page.page.weight = 30.0;
+
+       prefs_gtk_register_page((PrefsPage *) &rssyl_prefs_page);
+}
+
+void rssyl_prefs_done(void)
+{
+       prefs_gtk_unregister_page((PrefsPage *) &rssyl_prefs_page);
+}
+
+static void create_rssyl_prefs_page(PrefsPage *page,
+               GtkWindow *window, gpointer data)
+{
+       RSSylPrefsPage *prefs_page = (RSSylPrefsPage *) page;
+       GtkWidget *table;
+       GtkWidget *refresh;
+       GtkWidget *expired;
+       GtkWidget *refresh_on_startup;
+       GtkWidget *cookies_path;
+       GtkWidget *label;
+       GtkObject *refresh_adj, *expired_adj;
+
+       table = gtk_table_new(RSSYL_NUM_PREFS, 2, FALSE);
+       gtk_container_set_border_width(GTK_CONTAINER(table), 5);
+       gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
+       gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+       label = gtk_label_new(_("Default refresh interval in minutes"));
+       gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
+                       GTK_FILL, 0, 0, 0);
+
+       refresh_adj = gtk_adjustment_new(rssyl_prefs.refresh,
+                       0, 100000, 1, 10, 0);
+       refresh = gtk_spin_button_new(GTK_ADJUSTMENT(refresh_adj), 1, 0);
+       gtk_table_attach(GTK_TABLE(table), refresh, 1, 2, 0, 1,
+                       GTK_FILL, 0, 0, 0);
+       CLAWS_SET_TIP(refresh,
+                       _("Set to 0 to disable automatic refreshing"));
+
+       label = gtk_label_new(_("Default number of expired items to keep"));
+       gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
+                       GTK_FILL, 0, 0, 0);
+
+       expired_adj = gtk_adjustment_new(rssyl_prefs.expired,
+                       -1, 100000, 1, 10, 0);
+       expired = gtk_spin_button_new(GTK_ADJUSTMENT(expired_adj), 1, 0);
+       gtk_table_attach(GTK_TABLE(table), expired, 1, 2, 1, 2,
+                       GTK_FILL, 0, 0, 0);
+       CLAWS_SET_TIP(expired,
+                       _("Set to -1 to keep expired items"));
+
+       refresh_on_startup = gtk_check_button_new_with_label(
+                       _("Refresh all feeds on application start"));
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(refresh_on_startup),
+                       rssyl_prefs.refresh_on_startup);
+       gtk_table_attach(GTK_TABLE(table), refresh_on_startup, 0, 2, 3, 4,
+                       GTK_FILL | GTK_EXPAND, 0, 0, 0);
+
+       label = gtk_label_new(_("Path to cookies file"));
+       gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
+                       GTK_FILL, 0, 0, 0);
+       gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
+
+       cookies_path = gtk_entry_new ();
+       gtk_entry_set_text(GTK_ENTRY(cookies_path), rssyl_prefs.cookies_path);
+       gtk_table_attach(GTK_TABLE(table), cookies_path, 1, 2, 4, 5,
+                       GTK_FILL | GTK_EXPAND, 0, 0, 0);
+       CLAWS_SET_TIP(cookies_path,
+                       _("Path to Netscape-style cookies.txt file containing your cookies"));
+
+       gtk_widget_show_all(table);
+
+       prefs_page->page.widget = table;
+       prefs_page->refresh = refresh;
+       prefs_page->expired = expired;
+       prefs_page->refresh_on_startup = refresh_on_startup;
+       prefs_page->cookies_path = cookies_path;
+}
+
+static void destroy_rssyl_prefs_page(PrefsPage *page)
+{
+       /* Do nothing! */
+}
+
+static void save_rssyl_prefs(PrefsPage *page)
+{
+       RSSylPrefsPage *prefs_page = (RSSylPrefsPage *)page;
+       PrefFile *pref_file;
+       gchar *rc_file_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+                       COMMON_RC, NULL);
+
+       rssyl_prefs.refresh = gtk_spin_button_get_value_as_int(
+                       GTK_SPIN_BUTTON(prefs_page->refresh));
+       rssyl_prefs.expired = gtk_spin_button_get_value_as_int(
+                       GTK_SPIN_BUTTON(prefs_page->expired));
+       rssyl_prefs.refresh_on_startup = gtk_toggle_button_get_active(
+                       GTK_TOGGLE_BUTTON(prefs_page->refresh_on_startup));
+       g_free(rssyl_prefs.cookies_path);
+       rssyl_prefs.cookies_path = g_strdup(gtk_entry_get_text(
+                       GTK_ENTRY(prefs_page->cookies_path)));
+
+       pref_file = prefs_write_open(rc_file_path);
+       g_free(rc_file_path);
+
+       if( !pref_file || prefs_set_block_label(pref_file, PREFS_BLOCK_NAME) < 0 )
+                               return;
+
+       if( prefs_write_param(param, pref_file->fp) < 0 ) {
+               g_warning("Failed to write RSSyl plugin configuration\n");
+               prefs_file_close_revert(pref_file);
+               return;
+       }
+
+        if (fprintf(pref_file->fp, "\n") < 0) {
+               FILE_OP_ERROR(rc_file_path, "fprintf");
+               prefs_file_close_revert(pref_file);
+       } else
+               prefs_file_close(pref_file);
+}
+
+RSSylPrefs *rssyl_prefs_get(void)
+{
+       return &rssyl_prefs;
+}
diff --git a/src/plugins/rssyl/rssyl_prefs.h b/src/plugins/rssyl/rssyl_prefs.h
new file mode 100644 (file)
index 0000000..ba30777
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __RSSYL_PREFS
+#define __RSSYL_PREFS
+
+#define PREFS_BLOCK_NAME       "rssyl"
+
+#define RSSYL_NUM_PREFS                4
+
+#define RSSYL_PREF_DEFAULT_REFRESH     "180"
+#define RSSYL_PREF_DEFAULT_EXPIRED     "-1"
+
+typedef struct _RSSylPrefs RSSylPrefs;
+
+struct _RSSylPrefs {
+       gint refresh;
+       gint expired;
+       gboolean refresh_on_startup;
+       gchar *cookies_path;
+};
+
+typedef struct _RSSylPrefsPage RSSylPrefsPage;
+
+struct _RSSylPrefsPage {
+       PrefsPage page;
+       GtkWidget *refresh;
+       GtkWidget *expired;
+       GtkWidget *refresh_on_startup;
+       GtkWidget *cookies_path;
+};
+
+void rssyl_prefs_init(void);
+void rssyl_prefs_done(void);
+RSSylPrefs *rssyl_prefs_get(void);
+
+#endif /* __RSSYL_PREFS */
diff --git a/src/plugins/rssyl/strreplace.c b/src/plugins/rssyl/strreplace.c
new file mode 100644 (file)
index 0000000..7d4322c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2005 Andrej Kacian <andrej@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
new file mode 100644 (file)
index 0000000..37c29dc
--- /dev/null
@@ -0,0 +1,8 @@
+#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/version.rc b/src/plugins/rssyl/version.rc
new file mode 100644 (file)
index 0000000..7e75b30
--- /dev/null
@@ -0,0 +1,36 @@
+1 VERSIONINFO
+ FILEVERSION 0, 0, 0, 0
+ PRODUCTVERSION 0, 0, 0, 0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "000004b0"
+        BEGIN
+            VALUE "FileDescription", "RSSyl Plugin\0"
+            VALUE "FileVersion", "0.0.0.0\0"
+            VALUE "ProductVersion", "0.0.0.0 Win32\0"
+            VALUE "LegalCopyright", "GPL / © 1999-2008 Hiroyuki Yamamoto & The Claws Mail Team\0"
+            VALUE "CompanyName", "GNU / Free Software Foundation\0"
+            VALUE "ProductName", "Claws Mail\0"
+//            VALUE "Comments", "\0"
+//            VALUE "InternalName", "\0"
+//            VALUE "LegalTrademarks", "\0"
+//            VALUE "OriginalFilename", "\0"
+//            VALUE "PrivateBuild", "\0"
+//            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0, 1200
+    END
+END
index baca92a..3f32908 100644 (file)
@@ -78,12 +78,14 @@ vcalendar_la_DEPENDENCIES = $(plugin_deps)
 
 vcalendar_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
        $(GTK_LIBS) \
+       $(CURL_LIBS) \
         $(top_builddir)/src/plugins/vcalendar/libical/libical/libical.la
 
 AM_CPPFLAGS = \
        $(CLAWS_MAIL_CFLAGS) \
        $(GLIB_CFLAGS) \
        $(GTK_CFLAGS) \
+       $(CURL_CFLAGS) \
        -DLOCALEDIR=\""$(localedir)"\"
 
 clean-local:
index e009ae1..5e0cf42 100644 (file)
@@ -258,6 +258,8 @@ LIBS = @LIBS@
 LIBSOUP_GNOME_CFLAGS = @LIBSOUP_GNOME_CFLAGS@
 LIBSOUP_GNOME_LIBS = @LIBSOUP_GNOME_LIBS@
 LIBTOOL = @LIBTOOL@
+LIBXML_CFLAGS = @LIBXML_CFLAGS@
+LIBXML_LIBS = @LIBXML_LIBS@
 LIPO = @LIPO@
 LN_S = @LN_S@
 LTLIBICONV = @LTLIBICONV@
@@ -296,6 +298,7 @@ PACKAGE_URL = @PACKAGE_URL@
 PACKAGE_VERSION = @PACKAGE_VERSION@
 PASSCRYPT_KEY = @PASSCRYPT_KEY@
 PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
 PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
@@ -434,12 +437,14 @@ vcalendar_la_LDFLAGS = \
 vcalendar_la_DEPENDENCIES = $(plugin_deps)
 vcalendar_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
        $(GTK_LIBS) \
+       $(CURL_LIBS) \
         $(top_builddir)/src/plugins/vcalendar/libical/libical/libical.la
 
 AM_CPPFLAGS = \
        $(CLAWS_MAIL_CFLAGS) \
        $(GLIB_CFLAGS) \
        $(GTK_CFLAGS) \
+       $(CURL_CFLAGS) \
        -DLOCALEDIR=\""$(localedir)"\"
 
 all: all-recursive
index c40acea..70f4e53 100644 (file)
@@ -2,6 +2,6 @@
 #define ICAL_VERSION_H
 
 #define ICAL_PACKAGE "claws-mail"
-#define ICAL_VERSION "3.9.0cvs64"
+#define ICAL_VERSION "3.9.0cvs69"
 
 #endif
index ce28372..023620b 100644 (file)
@@ -23,7 +23,6 @@
 #include "claws-features.h"
 #endif
 
-#include <stddef.h>
 #include <glib.h>
 #include <glib/gi18n.h>
 
index 417be62..5abdadc 100644 (file)
@@ -839,6 +839,9 @@ static PrefParam param[] = {
 #ifndef G_OS_WIN32
        {"uri_open_command", DEFAULT_BROWSER_CMD,
         &SPECIFIC_PREFS.uri_cmd, P_STRING, NULL, NULL, NULL},
+#else
+       {"gtk_theme", DEFAULT_W32_GTK_THEME,
+        &SPECIFIC_PREFS.gtk_theme, P_STRING, NULL, NULL, NULL},
 #endif
        {"ext_editor_command", DEFAULT_EDITOR_CMD,
         &SPECIFIC_PREFS.ext_editor_cmd, P_STRING, NULL, NULL, NULL},
index b144a16..dd5d094 100644 (file)
@@ -378,6 +378,7 @@ struct _PrefsCommon
        /* Other */
 #ifndef G_OS_WIN32
        gchar *uri_cmd;
+       gchar *gtk_theme;
 #endif
        gchar *ext_editor_cmd;
        gboolean cmds_use_system_default;