update copyright year
[claws.git] / src / plugins / vcalendar / vcal_folder.c
index e18240a95a9dbe0e1ded0b13df2e4ffaea6ecb66..1581691a8351a5658aca29603a4565d623783b8e 100644 (file)
@@ -1,7 +1,6 @@
 /*
- * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2007 Colin Leroy <colin@colino.net> and 
- * the Claws Mail team
+ * Claws Mail -- a GTK based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2024 the Claws Mail team and Colin Leroy <colin@colino.net>
  *
  * 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
@@ -14,8 +13,7 @@
  * 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.
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <unistd.h>
 #include <curl/curl.h>
 #include <curl/curlver.h>
+#include <ctype.h>
 
 #include "account.h"
+#include "file-utils.h"
 #include "utils.h"
 #include "procmsg.h"
 #include "procheader.h"
 #include "mainwindow.h"
 #include "statusbar.h"
 #include "msgcache.h"
+#include "passwordstore.h"
 #include "timing.h"
 #include "messageview.h"
 
 #include <gtk/gtk.h>
-#include <dirent.h>
-
-#define VCAL_FOLDERITEM(item) ((VCalFolderItem *) item)
 
 #ifdef USE_PTHREAD
 #include <pthread.h>
@@ -131,6 +129,7 @@ static void update_subscription(const gchar *uri, gboolean verbose);
 static void vcal_folder_set_batch      (Folder         *folder,
                                         FolderItem     *item,
                                         gboolean        batch);
+static void convert_to_utc(icalcomponent *calendar);
 
 gboolean vcal_subscribe_uri(Folder *folder, const gchar *uri);
 
@@ -164,7 +163,7 @@ static char *vcal_popup_labels[] =
 {
        N_("_New meeting..."),
        N_("_Export calendar..."),
-       N_("_Subscribe to webCal..."),
+       N_("_Subscribe to Webcal..."),
        N_("_Unsubscribe..."),
        N_("_Rename..."),
        N_("U_pdate subscriptions"),
@@ -235,7 +234,7 @@ static void vcal_fill_popup_menu_labels(void)
 
 static FolderViewPopup vcal_popup =
 {
-       "vCalendar",
+       PLUGIN_NAME,
        "<vCalendar>",
        vcal_popup_entries,
        G_N_ELEMENTS(vcal_popup_entries),
@@ -330,7 +329,7 @@ static void vcal_item_opened(FolderItem *item)
 
 void vcal_folder_refresh_cal(FolderItem *item)
 {
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        if (item->folder != folder)
                return;
        if (((VCalFolderItem *)(item))->dw)
@@ -356,8 +355,8 @@ FolderClass *vcal_folder_get_class()
        if (vcal_class.idstr == NULL) {
                debug_print("register class\n");
                vcal_class.type = F_UNKNOWN;
-               vcal_class.idstr = "vCalendar";
-               vcal_class.uistr = "vCalendar";
+               vcal_class.idstr = PLUGIN_NAME;
+               vcal_class.uistr = PLUGIN_NAME;
 
                /* Folder functions */
                vcal_class.new_folder = vcal_folder_new;
@@ -609,7 +608,8 @@ add_new:
                        }
                        if (rprop && ritr) {
                                struct icaldurationtype ical_dur;
-                               struct icaltimetype dtstart, dtend;
+                               struct icaltimetype dtstart = icaltime_null_time();
+                               struct icaltimetype dtend = icaltime_null_time();
                                evt = icalcomponent_new_clone(evt);
                                prop = icalcomponent_get_first_property(evt, ICAL_RRULE_PROPERTY);
                                if (prop) {
@@ -619,9 +619,13 @@ add_new:
                                prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
                                if (prop)
                                        dtstart = icalproperty_get_dtstart(prop);
+                               else
+                                       debug_print("event has no DTSTART!\n");
                                prop = icalcomponent_get_first_property(evt, ICAL_DTEND_PROPERTY);
                                if (prop)
                                        dtend = icalproperty_get_dtend(prop);
+                               else
+                                       debug_print("event has no DTEND!\n");
                                ical_dur = icaltime_subtract(dtend, dtstart);
                                next = icalrecur_iterator_next(ritr);
                                if (!icaltime_is_null_time(next) &&
@@ -706,9 +710,10 @@ add_new:
 
 GSList *vcal_get_events_list(FolderItem *item)
 {
-       DIR *dp;
-       struct dirent *d;
+       GDir *dp;
+       const gchar *d;
        GSList *events = NULL;
+       GError *error = NULL;
 
        if (item != item->folder->inbox) {
                GSList *subs = vcal_folder_get_webcal_events_for_folder(item);
@@ -725,27 +730,29 @@ GSList *vcal_get_events_list(FolderItem *item)
                return events;
        }
 
-       dp = opendir(vcal_manager_get_event_path());
+       dp = g_dir_open(vcal_manager_get_event_path(), 0, &error);
        
        if (!dp) {
-               FILE_OP_ERROR(vcal_manager_get_event_path(), "opendir");
+               debug_print("couldn't open dir '%s': %s (%d)\n",
+                               vcal_manager_get_event_path(), error->message, error->code);
+               g_error_free(error);
                return 0;
        }
 
-       while ((d = readdir(dp)) != NULL) {
+       while ((d = g_dir_read_name(dp)) != NULL) {
                VCalEvent *event = NULL;
-               if (d->d_name[0] == '.' || strstr(d->d_name, ".bak")
-               ||  !strcmp(d->d_name, "internal.ics")
-               ||  !strcmp(d->d_name, "internal.ifb")
-               ||  !strcmp(d->d_name, "multisync")) 
+               if (d[0] == '.' || strstr(d, ".bak")
+               ||  !strcmp(d, "internal.ics")
+               ||  !strcmp(d, "internal.ifb")
+               ||  !strcmp(d, "multisync")) 
                        continue;
 
-               event = vcal_manager_load_event(d->d_name);
+               event = vcal_manager_load_event(d);
                if (!event)
                        continue;
-               if (event->rec_occurence) {
+               if (event->rec_occurrence) {
                        vcal_manager_free_event(event);
-                       claws_unlink(d->d_name);
+                       claws_unlink(d);
                        continue;
                }
 
@@ -771,7 +778,7 @@ GSList *vcal_get_events_list(FolderItem *item)
                                struct icaldurationtype ical_dur;
                                int i = 0;
 
-                               debug_print("dumping recurring events from main event %s\n", d->d_name);
+                               debug_print("dumping recurring events from main event %s\n", d);
                                recur = icalrecurrencetype_from_string(event->recur);
                                dtstart = icaltime_from_string(event->dtstart);
 
@@ -787,7 +794,7 @@ GSList *vcal_get_events_list(FolderItem *item)
                                        next = icalrecur_iterator_next(ritr);
                                debug_print("next time is %snull\n", icaltime_is_null_time(next)?"":"not ");
                                while (!icaltime_is_null_time(next) && i < 100) {
-                                       gchar *new_start = NULL, *new_end = NULL;
+                                       const gchar *new_start = NULL, *new_end = NULL;
                                        VCalEvent *nevent = NULL;
                                        gchar *uid = g_strdup_printf("%s-%d", event->uid, i);
                                        new_start = icaltime_as_ical_string(next);
@@ -801,7 +808,7 @@ GSList *vcal_get_events_list(FolderItem *item)
                                                                event->sequence, event->type);
                                        g_free(uid);
                                        vcal_manager_copy_attendees(event, nevent);
-                                       nevent->rec_occurence = TRUE;
+                                       nevent->rec_occurrence = TRUE;
                                        vcal_manager_save_event(nevent, FALSE);
                                        account = vcal_manager_get_account_from_event(event);
                                        status =
@@ -822,7 +829,7 @@ GSList *vcal_get_events_list(FolderItem *item)
                        vcal_manager_free_event(event);
                }
        }
-       closedir(dp);
+       g_dir_close(dp);
        return g_slist_reverse(events);
 }
 
@@ -860,7 +867,7 @@ static gint vcal_get_num_list(Folder *folder, FolderItem *item,
                        continue;
                g_hash_table_insert(hash_uids, GINT_TO_POINTER(n_msg), g_strdup(event->uid));
                
-               if (event->rec_occurence) {
+               if (event->rec_occurrence) {
                        vcal_manager_free_event(event);
                        continue;
                }
@@ -1051,7 +1058,7 @@ static gint vcal_remove_msg(Folder *folder, FolderItem *_item, gint num)
        if (_item == folder->inbox)
                vcal_remove_event(folder, msginfo);
 
-       procmsg_msginfo_free(msginfo);
+       procmsg_msginfo_free(&msginfo);
        return 0;
 }
 
@@ -1124,7 +1131,7 @@ static gint vcal_remove_folder(Folder *folder, FolderItem *fitem)
 
 static gboolean vcal_scan_required(Folder *folder, FolderItem *item)
 {
-       struct stat s;
+       GStatBuf s;
        VCalFolderItem *vitem = (VCalFolderItem *)item;
 
        g_return_val_if_fail(item != NULL, FALSE);
@@ -1144,7 +1151,7 @@ static gint vcal_folder_lock_count = 0;
 
 static void vcal_set_mtime(Folder *folder, FolderItem *item)
 {
-       struct stat s;
+       GStatBuf s;
        gchar *path = folder_item_get_path(item);
 
        if (folder->inbox != item)
@@ -1159,7 +1166,8 @@ static void vcal_set_mtime(Folder *folder, FolderItem *item)
        }
 
        item->mtime = s.st_mtime;
-       debug_print("VCAL: forced mtime of %s to %ld\n", item->name?item->name:"(null)", item->mtime);
+       debug_print("VCAL: forced mtime of %s to %"CM_TIME_FORMAT"\n",
+                       item->name?item->name:"(null)", item->mtime);
        g_free(path);
 }
 
@@ -1167,31 +1175,45 @@ void vcal_folder_export(Folder *folder)
 {      
        FolderItem *item = folder?folder->inbox:NULL;
        gboolean need_scan = folder?vcal_scan_required(folder, item):TRUE;
+       gchar *export_pass = NULL;
+       gchar *export_freebusy_pass = NULL;
 
        if (vcal_folder_lock_count) /* blocked */
                return;
        vcal_folder_lock_count++;
+       
+       export_pass = vcal_passwd_get("export");
+       export_freebusy_pass = vcal_passwd_get("export_freebusy");
+
        if (vcal_meeting_export_calendar(vcalprefs.export_path, 
                        vcalprefs.export_user, 
-                       vcalprefs.export_pass,
+                       export_pass,
                        TRUE)) {
                debug_print("exporting calendar\n");
                if (vcalprefs.export_enable &&
                    vcalprefs.export_command &&
                    strlen(vcalprefs.export_command))
                        execute_command_line(
-                               vcalprefs.export_command, TRUE);
+                               vcalprefs.export_command, TRUE, NULL);
+       }
+       if (export_pass != NULL) {
+               memset(export_pass, 0, strlen(export_pass));
        }
+       g_free(export_pass);
        if (vcal_meeting_export_freebusy(vcalprefs.export_freebusy_path,
                        vcalprefs.export_freebusy_user,
-                       vcalprefs.export_freebusy_pass)) {
+                       export_freebusy_pass)) {
                debug_print("exporting freebusy\n");
                if (vcalprefs.export_freebusy_enable &&
                    vcalprefs.export_freebusy_command &&
                    strlen(vcalprefs.export_freebusy_command))
                        execute_command_line(
-                               vcalprefs.export_freebusy_command, TRUE);
+                               vcalprefs.export_freebusy_command, TRUE, NULL);
        }
+       if (export_freebusy_pass != NULL) {
+               memset(export_freebusy_pass, 0, strlen(export_freebusy_pass));
+       }
+       g_free(export_freebusy_pass);
        vcal_folder_lock_count--;
        if (!need_scan && folder) {
                vcal_set_mtime(folder, folder->inbox);
@@ -1270,7 +1292,8 @@ void vcal_folder_gtk_done(void)
                if (!file)                      
                        continue;
                debug_print("removing %s\n", file);
-               g_unlink(file);
+               if (g_unlink(file) < 0)
+                        FILE_OP_ERROR(file, "g_unlink");
                g_free(file);
        }
        g_slist_free(created_files);
@@ -1329,7 +1352,7 @@ static void new_meeting_cb(GtkAction *action, gpointer data)
 
 GSList * vcal_folder_get_waiting_events(void)
 {
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        return vcal_get_events_list(folder->inbox);
 }
 
@@ -1363,7 +1386,7 @@ static gboolean get_webcal_events_func(GNode *node, gpointer user_data)
 GSList * vcal_folder_get_webcal_events(void)
 {
        GetWebcalData *data = g_new0(GetWebcalData, 1);
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        GSList *list = NULL;
        data->item = NULL;
        g_node_traverse(folder->node, G_PRE_ORDER,
@@ -1399,7 +1422,7 @@ static gboolean vcal_free_data_func(GNode *node, gpointer user_data)
 
 void vcal_folder_free_data(void)
 {
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
 
        g_node_traverse(folder->node, G_PRE_ORDER,
                        G_TRAVERSE_ALL, -1, vcal_free_data_func, NULL);
@@ -1408,7 +1431,7 @@ void vcal_folder_free_data(void)
 GSList * vcal_folder_get_webcal_events_for_folder(FolderItem *item)
 {
        GetWebcalData *data = g_new0(GetWebcalData, 1);
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        GSList *list = NULL;
        data->item = item;
        g_node_traverse(folder->node, G_PRE_ORDER,
@@ -1445,14 +1468,13 @@ gchar* get_item_event_list_for_date(FolderItem *item, EventTime date)
                        if (days != date)
                                continue;
                        prop = icalcomponent_get_first_property((icalcomponent *)fdata->event, ICAL_SUMMARY_PROPERTY);
-                       if (prop) {
-                               if (!g_utf8_validate(icalproperty_get_summary(prop), -1, NULL))
-                                       summary = conv_codeset_strdup(icalproperty_get_summary(prop), 
+                       summary = g_strdup(icalproperty_get_summary(prop));
+                       if (summary) {
+                               if (!g_utf8_validate(summary, -1, NULL))
+                                       summary = conv_codeset_strdup(summary, 
                                                conv_get_locale_charset_str(), CS_UTF_8);
-                               else
-                                       summary = g_strdup(icalproperty_get_summary(prop));
                        } else
-                               summary = g_strdup("-");
+                               summary = g_strdup(_("[no summary]"));
 
                        strs = g_slist_prepend(strs, summary);
                }
@@ -1507,8 +1529,7 @@ gchar* get_item_event_list_for_date(FolderItem *item, EventTime date)
                        strcpy(result+e_len+2, (gchar *)cur->data);
                }
        }
-       slist_free_strings(strs);
-       g_slist_free(strs);
+       slist_free_strings_full(strs);
        return result;
 }
 
@@ -1525,17 +1546,20 @@ static size_t curl_recv(void *buf, size_t size, size_t nmemb, void *stream)
 {
        struct CBuf *buffer = (struct CBuf *)stream;
        gchar *tmp = NULL;
-       gchar tmpbuf[size*nmemb + 1];
+       gchar *tmpbuf = g_malloc0(size*nmemb + 1);
+
+       g_return_val_if_fail(tmpbuf != NULL, 0);
 
        memcpy(tmpbuf, buf, size*nmemb);
-       tmpbuf[size*nmemb] = '\0';
 
        if (buffer->str) {
+               /* If the buffer already has contents, append the new data. */
                tmp = g_strconcat(buffer->str, tmpbuf, NULL);
+               g_free(tmpbuf);
                g_free(buffer->str);
                buffer->str = tmp;
        } else {
-               buffer->str = g_strdup(tmpbuf);
+               buffer->str = tmpbuf;
        }
 
        return size*nmemb;
@@ -1567,6 +1591,9 @@ void *url_read_thread(void *data)
        curl_easy_setopt(curl_ctx, CURLOPT_WRITEDATA, &buffer);
        curl_easy_setopt(curl_ctx, CURLOPT_TIMEOUT, prefs_common_get_prefs()->io_timeout_secs);
        curl_easy_setopt(curl_ctx, CURLOPT_NOSIGNAL, 1);
+#ifdef G_OS_WIN32
+       curl_easy_setopt(curl_ctx, CURLOPT_CAINFO, claws_ssl_get_cert_file());
+#endif
 #if LIBCURL_VERSION_NUM >= 0x070a00
        if(vcalprefs.ssl_verify_peer == FALSE) {
                curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
@@ -1623,15 +1650,12 @@ gchar *vcal_curl_read(const char *url, const gchar *label, gboolean verbose,
        thread_data *td;
 #ifdef USE_PTHREAD
        pthread_t pt;
-       pthread_attr_t pta;
 #endif
        void *res;
-       gboolean killed;
        gchar *error = NULL;
        result = NULL;
        td = g_new0(thread_data, 1);
        res = NULL;
-       killed = FALSE;
 
        td->url  = url;
        td->result  = NULL;
@@ -1640,10 +1664,7 @@ gchar *vcal_curl_read(const char *url, const gchar *label, gboolean verbose,
        STATUSBAR_PUSH(mainwindow_get_mainwindow(), label);
 
 #ifdef USE_PTHREAD
-       if (pthread_attr_init(&pta) != 0 ||
-           pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE) != 0 ||
-           pthread_create(&pt, &pta, 
-                       url_read_thread, td) != 0) {
+       if (pthread_create(&pt, NULL, url_read_thread, td) != 0) {
                url_read_thread(td);    
        }
        while (!td->done)  {
@@ -1696,6 +1717,9 @@ gboolean vcal_curl_put(gchar *url, FILE *fp, gint filesize, const gchar *user, c
        curl_easy_setopt(curl_ctx, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl_ctx, CURLOPT_READDATA, fp);
        curl_easy_setopt(curl_ctx, CURLOPT_HTTPHEADER, headers);
+#ifdef G_OS_WIN32
+       curl_easy_setopt(curl_ctx, CURLOPT_CAINFO, claws_ssl_get_cert_file());
+#endif
 #if LIBCURL_VERSION_NUM >= 0x070a00
        if(vcalprefs.ssl_verify_peer == FALSE) {
                curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
@@ -1704,7 +1728,7 @@ gboolean vcal_curl_put(gchar *url, FILE *fp, gint filesize, const gchar *user, c
 #endif
        curl_easy_setopt(curl_ctx, CURLOPT_USERAGENT, 
                "Claws Mail vCalendar plugin "
-               "(http://www.claws-mail.org/plugins.php)");
+               "(" PLUGINS_URI ")");
        curl_easy_setopt(curl_ctx, CURLOPT_INFILESIZE, filesize);
        res = curl_easy_perform(curl_ctx);
        g_free(userpwd);
@@ -1717,7 +1741,7 @@ gboolean vcal_curl_put(gchar *url, FILE *fp, gint filesize, const gchar *user, c
 
        curl_easy_getinfo(curl_ctx, CURLINFO_RESPONSE_CODE, &response_code);
        if (response_code < 200 || response_code >= 300) {
-               g_warning("Can't export calendar, got code %ld\n", response_code);
+               g_warning("can't export calendar, got code %ld", response_code);
                res = FALSE;
        }
        curl_easy_cleanup(curl_ctx);
@@ -1742,7 +1766,7 @@ static gboolean folder_item_find_func(GNode *node, gpointer data)
 
 static FolderItem *get_folder_item_for_uri(const gchar *uri)
 {
-       Folder *root = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        gpointer d[2];
        
        if (root == NULL)
@@ -1760,29 +1784,20 @@ static gchar *feed_get_title(const gchar *str)
        gchar *title = NULL;
        if (strstr(str, "X-WR-CALNAME:")) {
                title = g_strdup(strstr(str, "X-WR-CALNAME:")+strlen("X-WR-CALNAME:"));
-               if (strstr(title, "\n"))
-                       *(strstr(title, "\n")) = '\0';
-               if (strstr(title, "\r"))
-                       *(strstr(title, "\r")) = '\0';          
        } else if (strstr(str, "X-WR-CALDESC:")) {
                title = g_strdup(strstr(str, "X-WR-CALDESC:")+strlen("X-WR-CALDESC:"));
-               if (strstr(title, "\n"))
-                       *(strstr(title, "\n")) = '\0';
-               if (strstr(title, "\r"))
-                       *(strstr(title, "\r")) = '\0';          
        }
-       
-       return title;
+       return strcrlftrunc(title);
 }
 
 static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean verbose, gchar *error)
 {
-       Folder *root = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        FolderItem *item = NULL;
        icalcomponent *cal = NULL;
        
        if (root == NULL) {
-               g_warning("can't get root folder\n");
+               g_warning("can't get root folder");
                g_free(feed);
                if (error)
                        g_free(error);
@@ -1790,9 +1805,10 @@ static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean v
        }
 
        if (feed == NULL) {
+               gchar *err_msg = _("Could not retrieve the Webcal URL:\n%s:\n\n%s");
+
                if (verbose && manual_update) {
-                       gchar *tmp; 
-                       tmp = g_strdup(uri);
+                       gchar *tmp = g_strdup(uri);
                        if (strlen(uri) > 61) {
                                tmp[55]='[';
                                tmp[56]='.';
@@ -1801,12 +1817,12 @@ static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean v
                                tmp[59]=']';
                                tmp[60]='\0';
                        } 
-                       alertpanel_error(_("Could not retrieve the Webcal URL:\n%s:\n\n%s"),
-                                       tmp, error ? error:_("Unknown error"));
+                       alertpanel_error(err_msg, tmp, error ? error:_("Unknown error"));
                        g_free(tmp);
                } else  {
-                       log_error(LOG_PROTOCOL, _("Could not retrieve the Webcal URL:\n%s:\n\n%s\n"),
-                                       uri, error ? error:_("Unknown error"));
+                       gchar *msg = g_strdup_printf("%s\n", err_msg);
+                       log_error(LOG_PROTOCOL, msg, uri, error ? error:_("Unknown error"));
+                       g_free(msg);
                }
                main_window_cursor_normal(mainwindow_get_mainwindow());
                g_free(feed);
@@ -1814,13 +1830,20 @@ static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean v
                        g_free(error);
                return;
        }
-       if (strncmp(feed, "BEGIN:VCALENDAR", strlen("BEGIN:VCALENDAR"))) {
+
+       gchar *tmp = feed;
+       while (*tmp && isspace((unsigned char)*tmp))
+               tmp++;
+
+       if (strncmp(tmp, "BEGIN:VCALENDAR", strlen("BEGIN:VCALENDAR"))) {
+               gchar *err_msg = _("This URL does not look like a Webcal URL:\n%s\n%s");
+
                if (verbose && manual_update) {
-                       alertpanel_error(_("This URL does not look like a WebCal URL:\n%s\n%s"),
-                                       uri, error ? error:_("Unknown error"));
+                       alertpanel_error(err_msg, uri, error ? error:_("Unknown error"));
                } else  {
-                       log_error(LOG_PROTOCOL, _("This URL does not look like a WebCal URL:\n%s\n%s\n"),
-                                       uri, error ? error:_("Unknown error"));
+                       gchar *msg = g_strdup_printf("%s\n", err_msg);
+                       log_error(LOG_PROTOCOL, msg, uri, error ? error:_("Unknown error"));
+                       g_free(msg);
                }
                g_free(feed);
                main_window_cursor_normal(mainwindow_get_mainwindow());
@@ -1836,16 +1859,25 @@ static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean v
                gchar *title = feed_get_title(feed);
                if (title == NULL) {
                        if (strstr(uri, "://"))
-                               title = g_strdup(strstr(uri,"://")+3);
+                               title = g_path_get_basename(strstr(uri,"://")+3);
                        else
                                title = g_strdup(uri);
                        subst_for_filename(title);
-                       if (strlen(title) > 32) {
-                               title[29]=title[30]=title[31]='.';
-                               title[32]='\0';
-                       }
                }
                item = folder_create_folder(root->node->data, title);
+               if (!item) {
+                       if (verbose && manual_update) {
+                               alertpanel_error(_("Could not create directory %s"),
+                                       title);
+                       } else  {
+                               log_error(LOG_PROTOCOL, _("Could not create directory %s"),
+                                       title);
+                       }
+                       g_free(feed);
+                       g_free(title);
+                       main_window_cursor_normal(mainwindow_get_mainwindow());
+                       return;
+               }
                debug_print("item done %s\n", title);
                ((VCalFolderItem *)item)->uri = g_strdup(uri);
                ((VCalFolderItem *)item)->feed = feed;
@@ -1858,6 +1890,8 @@ static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean v
                /* if title differs, update it */
        }
        cal = icalparser_parse_string(feed);
+
+       convert_to_utc(cal);
        
        if (((VCalFolderItem *)item)->cal)
                icalcomponent_free(((VCalFolderItem *)item)->cal);
@@ -1895,7 +1929,7 @@ static void update_subscription(const gchar *uri, gboolean verbose)
 
 static void check_subs_cb(GtkAction *action, gpointer data)
 {
-       Folder *root = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
 
        if (prefs_common_get_prefs()->work_offline && 
            !inc_offline_should_override(TRUE,
@@ -1910,8 +1944,45 @@ static void subscribe_cal_cb(GtkAction *action, gpointer data)
 {
        gchar *uri = NULL;
        gchar *tmp = NULL;
+       gchar *clip_text = NULL, *str = NULL;
+
+    clip_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
+
+    if (clip_text) {
+        str = clip_text;
+#if GLIB_CHECK_VERSION(2,66,0)
+        GError *error = NULL;
+        GUri *uri = NULL;
+
+        /* skip any leading white-space */
+        while (str && *str && g_ascii_isspace(*str))
+            str++;
+        uri = g_uri_parse(str, G_URI_FLAGS_PARSE_RELAXED, &error);
+        if (error) {
+            g_warning("could not parse clipboard text for URI: '%s'", error->message);
+            g_error_free(error);
+        }
+        if (uri) {
+            gchar* newstr = g_uri_to_string(uri);
+
+            debug_print("URI: '%s' -> '%s'\n", str, newstr ? newstr : "N/A");
+            if (newstr)
+                g_free(newstr);
+            g_uri_unref(uri);
+        } else {
+#else
+        if (!is_uri_string(str)) {
+#endif
+            /* if no URL, ignore clipboard text */
+            str = NULL;
+        }
+    }
+
+       tmp = input_dialog(_("Subscribe to Webcal"), _("Enter the Webcal URL:"), str ? str : "");
+
+       if (clip_text)
+               g_free(clip_text);
 
-       tmp = input_dialog(_("Subscribe to WebCal"), _("Enter the WebCal URL:"), NULL);
        if (tmp == NULL)
                return;
        
@@ -1937,39 +2008,36 @@ static void subscribe_cal_cb(GtkAction *action, gpointer data)
 static void unsubscribe_cal_cb(GtkAction *action, gpointer data)
 {
        FolderView *folderview = (FolderView *)data;
-       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
-       FolderItem *item;
+       FolderItem *item, *opened;
        gchar *message;
        AlertValue avalue;
-       gchar *old_path;
        gchar *old_id;
 
        if (!folderview->selected) return;
 
-       item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
+       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);
+       opened = folderview_get_opened_item(folderview);
 
        message = g_strdup_printf
                (_("Do you really want to unsubscribe?"));
-       avalue = alertpanel_full(_("Delete folder"), message,
-                                GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, 
-                                FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
+       avalue = alertpanel_full(_("Delete subscription"), message,
+                                NULL, _("_Cancel"), "edit-delete", _("_Delete"),
+                                NULL, NULL, ALERTFOCUS_FIRST,
+                                FALSE, NULL, ALERT_WARNING);
        g_free(message);
        if (avalue != G_ALERTALTERNATE) return;
 
-       Xstrdup_a(old_path, item->path, return);
        old_id = folder_item_get_identifier(item);
 
        vcal_item_closed(item);
 
-       if (folderview->opened == folderview->selected ||
-           gtk_cmctree_is_ancestor(ctree,
-                                 folderview->selected,
-                                 folderview->opened)) {
+       if (item == opened ||
+                       folder_is_child_of(item, opened)) {
                summary_clear_all(folderview->summaryview);
-               folderview->opened = NULL;
+               folderview_close_opened(folderview, TRUE);
        }
 
        if (item->folder->klass->remove_folder(item->folder, item) < 0) {
@@ -2054,14 +2122,16 @@ static void set_view_cb(GtkAction *gaction, GtkRadioAction *current, gpointer da
 {
        FolderView *folderview = (FolderView *)data;
        gint action = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
-       GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
        FolderItem *item = NULL, *oitem = NULL;
 
        if (!folderview->selected) return;
        if (setting_sensitivity) return;
 
-       oitem = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
-       item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
+       oitem = folderview_get_opened_item(folderview);
+       item = folderview_get_selected_item(folderview);
+
+       if (!item)
+               return;
 
        if (((VCalFolderItem *)(item))->use_cal_view == action)
                return;
@@ -2084,7 +2154,7 @@ gchar *vcal_get_event_as_ical_str(VCalEvent *event)
             icalproperty_new_prodid(
                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
            icalproperty_new_calscale("GREGORIAN"),
-            0);
+            (void*)0);
        vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
        ical = g_strdup(icalcomponent_as_ical_string(calendar));
        icalcomponent_free(calendar);
@@ -2122,6 +2192,56 @@ static gchar *get_email_from_property(icalproperty *p)
        return email;
 }
 
+static void convert_to_utc(icalcomponent *calendar)
+{
+       icalcomponent *event;
+       icaltimezone *tz, *tzutc = icaltimezone_get_utc_timezone();
+       icalproperty *prop;
+       icalparameter *tzid;
+
+       cm_return_if_fail(calendar != NULL);
+
+       for (
+                       event = icalcomponent_get_first_component(calendar,
+                               ICAL_VEVENT_COMPONENT);
+                       event != NULL;
+                       event = icalcomponent_get_next_component(calendar,
+                               ICAL_VEVENT_COMPONENT)) {
+
+               /* DTSTART */
+               if ((prop = icalcomponent_get_first_property(event, ICAL_DTSTART_PROPERTY)) != NULL
+                               && (tzid = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER)) != NULL) {
+                       /* Event has its DTSTART with a timezone specification, let's convert
+                        * to UTC and remove the TZID parameter. */
+
+                       tz = icalcomponent_get_timezone(calendar, icalparameter_get_iana_value(tzid));
+                       if (tz != NULL) {
+                               debug_print("Converting DTSTART to UTC.\n");
+                               icaltimetype t = icalproperty_get_dtstart(prop);
+                               icaltimezone_convert_time(&t, tz, tzutc);
+                               icalproperty_set_dtstart(prop, t);
+                               icalproperty_remove_parameter_by_ref(prop, tzid);
+                       }
+               }
+
+               /* DTEND */
+               if ((prop = icalcomponent_get_first_property(event, ICAL_DTEND_PROPERTY)) != NULL
+                               && (tzid = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER)) != NULL) {
+                       /* Event has its DTEND with a timezone specification, let's convert
+                        * to UTC and remove the TZID parameter. */
+
+                       tz = icalcomponent_get_timezone(calendar, icalparameter_get_iana_value(tzid));
+                       if (tz != NULL) {
+                               debug_print("Converting DTEND to UTC.\n");
+                               icaltimetype t = icalproperty_get_dtend(prop);
+                               icaltimezone_convert_time(&t, tz, tzutc);
+                               icalproperty_set_dtend(prop, t);
+                               icalproperty_remove_parameter_by_ref(prop, tzid);
+                       }
+               }
+       }
+}
+
 #define GET_PROP(comp,prop,kind) {                                             \
        prop = NULL;                                                            \
        if (!(prop = icalcomponent_get_first_property(comp, kind))) {           \
@@ -2175,7 +2295,7 @@ VCalEvent *vcal_get_event_from_ical(const gchar *ical, const gchar *charset)
        gchar *recur = NULL;
        int sequence = 0;
        enum icalproperty_method method = ICAL_METHOD_REQUEST;
-       enum icalproperty_kind type = ICAL_VEVENT_COMPONENT;
+       enum icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
        GSList *attendees = NULL;
        
        if (comp == NULL) {
@@ -2204,6 +2324,9 @@ VCalEvent *vcal_get_event_from_ical(const gchar *ical, const gchar *charset)
                TO_UTF8(summary);
                icalproperty_free(prop);
        }
+
+       convert_to_utc(comp);
+
        GET_PROP(comp, prop, ICAL_DTSTART_PROPERTY);
        if (prop) {
                dtstart = g_strdup(icaltime_as_ical_string(icalproperty_get_dtstart(prop)));
@@ -2334,13 +2457,13 @@ VCalEvent *vcal_get_event_from_ical(const gchar *ical, const gchar *charset)
 gboolean vcal_event_exists(const gchar *id)
 {
        MsgInfo *info = NULL;
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        if (!folder)
                return FALSE;
 
        info = folder_item_get_msginfo_by_msgid(folder->inbox, id);
        if (info != NULL) {
-               procmsg_msginfo_free(info);
+               procmsg_msginfo_free(&info);
                return TRUE;
        }
        return FALSE;
@@ -2370,7 +2493,7 @@ void vcal_foreach_event(gboolean (*cb_func)(const gchar *vevent))
 gboolean vcal_delete_event(const gchar *id)
 {
        MsgInfo *info = NULL;
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
        if (!folder)
                return FALSE;
 
@@ -2378,7 +2501,7 @@ gboolean vcal_delete_event(const gchar *id)
        if (info != NULL) {
                debug_print("removing event %s\n", id);
                vcal_remove_event(folder, info);
-               procmsg_msginfo_free(info);
+               procmsg_msginfo_free(&info);
                folder_item_scan(folder->inbox);
                return TRUE;
        }
@@ -2392,9 +2515,11 @@ gchar* vcal_add_event(const gchar *vevent)
 {
        VCalEvent *event = vcal_get_event_from_ical(vevent, NULL);
        gchar *retVal = NULL;
-       Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
-       if (!folder)
+       Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
+       if (!folder) {
+               vcal_manager_free_event(event);
                return NULL;
+       }
 
        if (event) {
                if (vcal_event_exists(event->uid)) {