RSSyl: fix OPML import creating incorrect folder hierarchy
[claws.git] / src / plugins / rssyl / rssyl_cb_menu.c
index f3047dea0b88c4dfc464d566112bb56801ff8c84..bb1104468ca8883d7ec5b4dc45680d3dbba39cbe 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Claws-Mail-- a GTK+ based, lightweight, and fast e-mail client
  * Copyright (C) 1999-2004 Hiroyuki Yamamoto
  * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
  *
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
-#include "claws-features.h"
 #endif
 
+/* Global includes */
 #include <glib.h>
 #include <glib/gi18n.h>
-
 #include <gtk/gtk.h>
 
-#include "folderview.h"
-#include "alertpanel.h"
-#include "gtk/inputdialog.h"
-#include "prefs_common.h"
-#include "filesel.h"
-#include "inc.h"
-#include "folder_item_prefs.h"
-
-#include "feed.h"
-#include "feedprops.h"
-#include "opml.h"
-#include "rssyl.h"
+/* Claws Mail includes */
+#include <folderview.h>
+#include <alertpanel.h>
+#include <gtk/inputdialog.h>
+#include <prefs_common.h>
+#include <folder_item_prefs.h>
+#include <filesel.h>
+#include <inc.h>
+
+/* Local includes */
+#include "libfeed/parser_opml.h"
 #include "rssyl_gtk.h"
-
-void rssyl_new_feed_cb(GtkAction *action, gpointer data)
+#include "rssyl_feed.h"
+#include "rssyl_feed_props.h"
+#include "rssyl_update_feed.h"
+#include "rssyl_subscribe.h"
+#include "opml_import.h"
+
+void rssyl_new_feed_cb(GtkAction *action,
+               gpointer data)
 {
-       FolderView *folderview = (FolderView *)data;
+       FolderView *folderview = (FolderView*)data;
        GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
        FolderItem *item;
-       gchar *new_feed;
+       gchar *url;
 
        debug_print("RSSyl: new_feed_cb\n");
 
@@ -59,26 +63,26 @@ void rssyl_new_feed_cb(GtkAction *action, gpointer data)
        g_return_if_fail(item != NULL);
        g_return_if_fail(item->folder != NULL);
 
-       new_feed = input_dialog(_("Subscribe feed"),
-                                        _("Input the URL of the news feed you wish to subscribe:"),
-                                        "");
-       g_return_if_fail(new_feed != NULL);
+       url = input_dialog(_("Subscribe feed"),
+                       _("Input the URL of the news feed you wish to subscribe:"),
+                       "");
+       if( url == NULL )       /* User cancelled */
+               return;
 
-       rssyl_subscribe_new_feed(item, new_feed, TRUE);
+       rssyl_subscribe(item, url, TRUE);
 
-       g_free(new_feed);
+       g_free(url);
 }
 
-void rssyl_new_folder_cb(GtkAction *action, gpointer data)
+void rssyl_new_folder_cb(GtkAction *action,
+               gpointer data)
 {
-       FolderView *folderview = (FolderView *)data;
+       FolderView *folderview = (FolderView*)data;
        GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
        FolderItem *item;
        FolderItem *new_item;
-       gchar *new_folder;
-       gchar *name;
-       gchar *p;
-       RSSylFolderItem *ritem = NULL;
+       gchar *new_folder, *p, *tmp;
+       gint i = 1;
 
        if (!folderview->selected) return;
 
@@ -90,132 +94,44 @@ void rssyl_new_folder_cb(GtkAction *action, gpointer data)
                                  _("Input the name of new folder:"),
                                  _("NewFolder"));
        if (!new_folder) return;
-       AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
 
        p = strchr(new_folder, G_DIR_SEPARATOR);
        if (p) {
-               alertpanel_error(_("'%c' can't be included in folder name."),
+               alertpanel_error(_("'%c' can't be used in folder name."),
                                 G_DIR_SEPARATOR);
+               g_free(new_folder);
                return;
        }
 
-       name = trim_string(new_folder, 32);
-       AUTORELEASE_STR(name, {g_free(name); return;});
-
-       /* find whether the directory already exists */
-       if (folder_find_child_item_by_name(item, new_folder)) {
-               alertpanel_error(_("The folder '%s' already exists."), name);
-               return;
+       /* Find an unused name for new folder */
+       /* TODO: Perhaps stop after X attempts? */
+       tmp = g_strdup(new_folder);
+       while (folder_find_child_item_by_name(item, tmp)) {
+               debug_print("RSSyl: Folder '%s' already exists, trying another name\n",
+                               new_folder);
+               g_free(tmp);
+               tmp = g_strdup_printf("%s__%d", new_folder, ++i);
        }
 
+       g_free(new_folder);
+       new_folder = tmp;
+
        new_item = folder_create_folder(item, new_folder);
        if (!new_item) {
-               alertpanel_error(_("Can't create the folder '%s'."), name);
-               return;
-       }
-
-       ritem = (RSSylFolderItem *)new_item;
-       ritem->url = NULL;
-
-       folder_write_list();
-}
-
-void rssyl_remove_rss_cb(GtkAction *action, gpointer data)
-{
-       FolderView *folderview = (FolderView *)data;
-       FolderItem *item;
-       gchar *name, *message;
-       AlertValue aval;
-
-       debug_print("RSSyl: remove_rss_cb\n");
-
-       item = folderview_get_selected_item(folderview);
-       g_return_if_fail(item != NULL);
-       g_return_if_fail(item->folder != NULL);
-       g_return_if_fail( !folder_item_parent(item) );
-
-       name = trim_string(item->folder->name, 32);
-       message = g_strdup_printf(_("Really remove the folder tree '%s'?\n"), name);
-       aval = alertpanel_full(_("Remove folder tree"), message,
-                        GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE, NULL,
-                         ALERT_WARNING, G_ALERTDEFAULT);
-       g_free(message);
-       g_free(name);
-
-       if (aval != G_ALERTALTERNATE)
-               return;
-
-       folderview_unselect(folderview);
-       summary_clear_all(folderview->summaryview);
-
-       folder_destroy(item->folder);
-}
-
-void rssyl_remove_feed_cb(GtkAction *action, gpointer data)
-{
-       FolderView *folderview = (FolderView *)data;
-       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
-       FolderItem *item;
-       gint response;
-       GtkWidget *dialog, *rmcache_widget = NULL;
-       gboolean rmcache = FALSE;
-       debug_print("RSSyl: remove_feed_cb\n");
-
-       item = folderview_get_selected_item(folderview);
-       g_return_if_fail(item != NULL);
-       g_return_if_fail(item->path != NULL);
-       g_return_if_fail(item->folder != NULL);
-
-       dialog = rssyl_feed_removal_dialog(item->name, &rmcache_widget);
-
-       g_return_if_fail(dialog != NULL);
-
-       gtk_widget_show_all(dialog);
-
-       response = gtk_dialog_run(GTK_DIALOG(dialog));
-
-       if( response != GTK_RESPONSE_YES ) {
-               debug_print("'No' clicked, returning\n");
-               gtk_widget_destroy(dialog);
+               alertpanel_error(_("Can't create the folder '%s'."), new_folder);
+               g_free(new_folder);
                return;
        }
 
-       g_return_if_fail(rmcache_widget != NULL);
-
-       rmcache = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(rmcache_widget) );
-
-       gtk_widget_destroy(dialog);
-
-       if( folderview->opened == folderview->selected ||
-                       gtk_cmctree_is_ancestor(ctree,
-                                       folderview->selected,
-                                       folderview->opened) ) {
-               summary_clear_all(folderview->summaryview);
-               folderview->opened = NULL;
-       }
-
-       rssyl_remove_feed_props((RSSylFolderItem *)item);
-
-       if( rmcache == TRUE )
-               rssyl_remove_feed_cache(item);
-
-       if( item->folder->klass->remove_folder(item->folder, item) < 0 ) {
-               gchar *tmp;
-               tmp = g_markup_printf_escaped(_("Can't remove feed '%s'."), item->name);
-               alertpanel_error("%s", tmp);
-               g_free(tmp);
-               if( folderview->opened == folderview->selected )
-                       summary_show(folderview->summaryview,
-                                       folderview->summaryview->folder_item);
-               return;
-       }
+       g_free(new_folder);
 
        folder_write_list();
 }
 
-void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
+void rssyl_remove_folder_cb(GtkAction *action,
+                            gpointer data)
 {
-       FolderView *folderview = (FolderView *)data;
+       FolderView *folderview = (FolderView*)data;
        GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
        FolderItem *item;
        gchar *message, *name;
@@ -263,45 +179,82 @@ void rssyl_remove_folder_cb(GtkAction *action, gpointer data)
 
 }
 
-void rssyl_refresh_cb(GtkAction *action, gpointer data)
+void rssyl_rename_cb(GtkAction *action,
+                            gpointer *data)
 {
-       FolderView *folderview = (FolderView *)data;
        FolderItem *item;
-       RSSylFolderItem *ritem;
-
+       gchar *new_folder;
+       gchar *name;
+       gchar *message;
+       FolderView *folderview = (FolderView*)data;
        item = folderview_get_selected_item(folderview);
        g_return_if_fail(item != NULL);
+       g_return_if_fail(item->path != NULL);
        g_return_if_fail(item->folder != NULL);
 
-       ritem = (RSSylFolderItem *)item;
+       name = trim_string(item->name, 32);
+       message = g_strdup_printf(_("Input new name for '%s':"), name);
+       new_folder = input_dialog(_("Rename folder"), message, name);
+       g_free(message);
+       g_free(name);
+       if (!new_folder) return;
+       AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+
+       if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
+               alertpanel_error(_("'%c' can't be included in folder name."),
+                                G_DIR_SEPARATOR);
+               return;
+       }
 
-       if (prefs_common_get_prefs()->work_offline && 
-          !inc_offline_should_override(TRUE,
-                       ngettext(
-                          "Claws Mail needs network access in order "
-                          "to update the feed.",
-                          "Claws Mail needs network access in order "
-                          "to update the feeds.", 1))) {
-                       return;
+       if (folder_find_child_item_by_name(folder_item_parent(item), new_folder)) {
+               name = trim_string(new_folder, 32);
+               alertpanel_error(_("The folder '%s' already exists."), name);
+               g_free(name);
+               return;
        }
 
-       main_window_cursor_wait(mainwindow_get_mainwindow());
-       rssyl_update_feed(ritem);
-       main_window_cursor_normal(mainwindow_get_mainwindow());
+       if (folder_item_rename(item, new_folder) < 0) {
+               alertpanel_error(_("The folder could not be renamed.\n"
+                                  "The new folder name is not allowed."));
+               return;
+       }
+
+       folder_item_prefs_save_config(item);
+       folder_write_list();
 }
 
-void rssyl_refresh_all_cb(GtkAction *action, gpointer data)
+void rssyl_refresh_feed_cb(GtkAction *action,
+               gpointer data)
 {
-       main_window_cursor_wait(mainwindow_get_mainwindow());
-       rssyl_refresh_all_feeds();
-       main_window_cursor_normal(mainwindow_get_mainwindow());
+       FolderView *folderview = (FolderView*)data;
+       FolderItem *item = NULL;
+       RFolderItem *ritem = NULL;
+
+       item = folderview_get_selected_item(folderview);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       ritem = (RFolderItem *)item;
+
+       /* Offline check */
+       if( prefs_common.work_offline &&
+                       !inc_offline_should_override(TRUE,
+                                       ngettext("Claws-Mail needs network access in order "
+                                       "to update the feed.",
+                                       "Claws-Mail needs network access in order "
+                                       "to update feeds.", 1))) {
+               return;
+       }
+
+       /* Update feed, displaying errors if any. */
+       rssyl_update_feed(ritem, TRUE);
 }
 
 void rssyl_prop_cb(GtkAction *action, gpointer data)
 {
-       FolderView *folderview = (FolderView *)data;
+       FolderView *folderview = (FolderView*)data;
        FolderItem *item;
-       RSSylFolderItem *ritem;
+       RFolderItem *ritem;
 
        item = folderview_get_selected_item(folderview);
        g_return_if_fail(item != NULL);
@@ -309,74 +262,106 @@ void rssyl_prop_cb(GtkAction *action, gpointer data)
 
        debug_print("RSSyl: rssyl_prop_cb() for '%s'\n", item->name);
 
-       ritem = (RSSylFolderItem *)item;
-       rssyl_get_feed_props(ritem);
+       ritem = (RFolderItem *)item;
+
        rssyl_gtk_prop(ritem);
 }
 
-void rssyl_rename_cb(GtkAction *action, gpointer data)
+void rssyl_update_all_cb( GtkAction *action, gpointer data)
 {
-       FolderView *folderview = (FolderView *)data;
        FolderItem *item;
-       gchar *new_folder;
-       gchar *name;
-       gchar *message;
+       FolderView *folderview = (FolderView*)data;
 
        item = folderview_get_selected_item(folderview);
        g_return_if_fail(item != NULL);
-       g_return_if_fail(item->path != NULL);
        g_return_if_fail(item->folder != NULL);
 
-       name = trim_string(item->name, 32);
-       message = g_strdup_printf(_("Input new name for '%s':"), name);
-       new_folder = input_dialog(_("Rename folder"), message, item->name);
-       g_free(message);
-       g_free(name);
-       if (!new_folder) return;
-       AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+       debug_print("RSSyl: rssyl_update_all_cb(): clicked on '%s'\n", item->name);
 
-       if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
-               alertpanel_error(_("'%c' can't be included in folder name."),
-                                G_DIR_SEPARATOR);
+       if( item->folder->klass != rssyl_folder_get_class() ) {
+               debug_print("RSSyl: this is not a RSSyl folder, returning\n");
                return;
        }
 
-       if (folder_find_child_item_by_name(folder_item_parent(item), new_folder)) {
-               name = trim_string(new_folder, 32);
-               alertpanel_error(_("The folder '%s' already exists."), name);
-               g_free(name);
+       rssyl_update_recursively(item);
+}
+
+void rssyl_remove_mailbox_cb(GtkAction *action, gpointer data)
+{
+       FolderView *folderview = (FolderView *)data;
+       FolderItem *item = NULL;
+       gchar *n, *message;
+       AlertValue avalue;
+
+       item = folderview_get_selected_item(folderview);
+
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->folder != NULL);
+
+       if( folder_item_parent(item) )
                return;
-       }
 
-       if (folder_item_rename(item, new_folder) < 0) {
-               alertpanel_error(_("The folder could not be renamed.\n"
-                                  "The new folder name is not allowed."));
+       n = trim_string(item->folder->name, 32);
+       message = g_strdup_printf(_("Really remove the feed tree `%s' ?\n"), n);
+       avalue = alertpanel_full(_("Remove feed tree"), message,
+                                GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE,
+                                NULL, ALERT_WARNING, G_ALERTDEFAULT);
+       g_free(message);
+       g_free(n);
+
+       if( avalue != G_ALERTALTERNATE )
+               return;
+
+       folderview_unselect(folderview);
+       summary_clear_all(folderview->summaryview);
+
+       n = folder_item_get_path(item);
+       if( remove_dir_recursive(n) < 0 ) {
+               g_warning("can't remove directory '%s'\n", n);
+               g_free(n);
                return;
        }
 
-       folder_item_prefs_save_config_recursive(item);
-       folder_write_list();
+       g_free(n);
+       folder_destroy(item->folder);
 }
 
 void rssyl_import_feed_list_cb(GtkAction *action, gpointer data)
 {
        FolderView *folderview = (FolderView *)data;
-       debug_print("RSSyl: rssyl_import_feed_cb\n");
+       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+       FolderItem *item = NULL;
+       gchar *path = NULL;
+       OPMLImportCtx *ctx = NULL;
 
-       FolderItem *item;
-       gchar *opmlfile = NULL;
+       debug_print("RSSyl: import_feed_list_cb\n");
 
-       item = folderview_get_selected_item(folderview);
+       /* Ask user for a file to import */
+       path = filesel_select_file_open_with_filter(
+                       _("Select an OPML file"), NULL, "*.opml");
+       if (!is_file_exist(path)) {
+               g_free(path);
+               return;
+       }
+
+       /* Find the destination folder for the import */
+       g_return_if_fail(folderview->selected != NULL);
+       item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
        g_return_if_fail(item != NULL);
        g_return_if_fail(item->folder != NULL);
 
-       opmlfile = filesel_select_file_open_with_filter(
-                       _("Select a .opml file"), NULL, "*.opml");
-       
-       if (!is_file_exist(opmlfile)) {
-               g_free(opmlfile);
-               return;
-       }
+       ctx = malloc( sizeof(OPMLImportCtx) );
+       ctx->failures = 0;
+       /* This needs to be +2, since we will be comparing it to depth of
+        * <outline> tag in OPML's XML structure. Topmost outlines are under
+        * <opml> and <body>, hence 2. */
+       ctx->depth = rssyl_folder_depth(item) + 2;
+       ctx->current = NULL;
+       ctx->current = g_slist_append(ctx->current, item);
+
+       /* Start the whole shebang - call libfeed's OPML parser with correct
+        * user function */
+       opml_process(path, rssyl_opml_import_func, (gpointer)ctx);
 
-       rssyl_opml_import(opmlfile, item);
+       g_free(ctx);
 }