/*
 * 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
 *
 * 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 3 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 "defs.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glob.h>
#include <unistd.h>
#include "gettext.h"
#include <curl/curl.h>
#include <curl/curlver.h>

#include "account.h"
#include "utils.h"
#include "procmsg.h"
#include "procheader.h"
#include "folder.h"
#include "folderview.h"
#include "vcalendar.h"
#include "vcal_folder.h"
#include "vcal_prefs.h"
#include "vcal_manager.h"
#include "vcal_meeting_gtk.h"
#include "prefs_account.h"
#include "prefs_common.h"
#include "inputdialog.h"
#include "xml.h"
#include "alertpanel.h"
#include "log.h"
#include "mainwindow.h"
#include "statusbar.h"

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

#define VCAL_FOLDERITEM(item) ((VCalFolderItem *) item)

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

typedef struct _thread_data {
	const gchar *url;
	gchar *result;
	gchar *error;
	gboolean done;
} thread_data;

typedef struct _IcalFeedData {
	icalcomponent *event;
	gchar *pseudoevent_id;
} IcalFeedData;

typedef struct _VCalFolder VCalFolder;
typedef struct _VCalFolderItem VCalFolderItem;

static Folder *vcal_folder_new(const gchar * name,
				  const gchar * folder);
static void vcal_folder_destroy(Folder * folder);
static void vcal_item_destroy(Folder *folder, FolderItem *_item);
static gchar *vcal_item_get_path(Folder *folder, FolderItem *item);

static gint vcal_scan_tree(Folder * folder);
static FolderItem *vcal_item_new(Folder * folder);
static gint vcal_get_num_list(Folder * folder, FolderItem * item,
				 MsgNumberList ** list,
				 gboolean * old_uids_valid);
static MsgInfo *vcal_get_msginfo(Folder * folder, FolderItem * item,
				    gint num);
static gchar *vcal_fetch_msg(Folder * folder, FolderItem * item,
				gint num);
static gint vcal_add_msg(Folder * folder, FolderItem * _dest,
			    const gchar * file, MsgFlags * flags);
static gint vcal_remove_msg(Folder * folder, FolderItem * _item,
			       gint num);
static FolderItem *vcal_create_folder(Folder * folder,
					 FolderItem * parent,
					 const gchar * name);
static gint vcal_create_tree(Folder *folder);
static gint vcal_remove_folder(Folder *folder, FolderItem *item);
static gboolean vcal_scan_required(Folder *folder, FolderItem *item);
static void vcal_change_flags(Folder *folder, FolderItem *_item, MsgInfo *msginfo, MsgPermFlags newflags);
static void new_meeting_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void export_cal_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void subscribe_cal_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void check_subs_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void unsubscribe_cal_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void set_sensitivity(GtkItemFactory *factory, FolderItem *item);
static void update_subscription(const gchar *uri, gboolean verbose);
static void rename_cb(FolderView *folderview, guint action, GtkWidget *widget);
static void vcal_folder_set_batch	(Folder		*folder,
					 FolderItem	*item,
					 gboolean	 batch);

gboolean vcal_subscribe_uri(Folder *folder, const gchar *uri);

FolderClass vcal_class;

static GSList *created_files = NULL;
static GHashTable *hash_uids = NULL;

struct _VCalFolder
{
	Folder folder;
};

struct _VCalFolderItem
{
	FolderItem item;
	gchar *uri;
	gchar *feed;
	icalcomponent *cal;
	GSList *numlist;
	GSList *evtlist;
	gboolean batching;
	gboolean dirty;
};

static char *vcal_popup_labels[] =
{
	N_("/_New meeting..."),
	N_("/_Export calendar..."),
	"/---",
	N_("/_Subscribe to webCal..."),
	N_("/_Unsubscribe..."),
	"/---",
	N_("/_Rename..."),
	"/---",
	N_("/U_pdate subscriptions"),
	"/---",
	NULL
};			
static GtkItemFactoryEntry vcal_popup_entries[] =
{
	{NULL, NULL, new_meeting_cb,	0, NULL},
	{NULL, NULL, export_cal_cb,	0, NULL},
	{NULL, NULL, NULL,    	0, "<Separator>"},
	{NULL, NULL, subscribe_cal_cb,	0, NULL},
	{NULL, NULL, unsubscribe_cal_cb,	0, NULL},
	{NULL, NULL, NULL,    	0, "<Separator>"},
	{NULL, NULL, rename_cb, 0, NULL},
	{NULL, NULL, NULL,    	0, "<Separator>"},
	{NULL, NULL, check_subs_cb, 0, NULL},
	{NULL, NULL, NULL,  	0, "<Separator>"}
};

static IcalFeedData *icalfeeddata_new(icalcomponent *evt, gchar *str)
{
	IcalFeedData *data = g_new0(IcalFeedData, 1);
	if (str)
		data->pseudoevent_id = g_strdup(str);
	data->event = evt;
	return data;
}

static void icalfeeddata_free(IcalFeedData *data)
{
	g_free(data->pseudoevent_id);
	g_free(data);
}

static void slist_free_icalfeeddata(GSList *list)
{
	while (list) {
		icalfeeddata_free(list->data);
		list = list->next;
	}
}

static void vcal_fill_popup_menu_labels(void)
{
	gint i;

	for( i = 0; vcal_popup_labels[i] != NULL; i++ ) {
		(vcal_popup_entries[i]).path = _(vcal_popup_labels[i]);
	}
}

static FolderViewPopup vcal_popup =
{
	"vCalendar",
	"<vCalendar>",
	NULL,
	set_sensitivity
};

static void vcal_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
{
	GList *cur;
	folder_item_set_xml(folder, item, tag);
	
	for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
		XMLAttr *attr = (XMLAttr *) cur->data;

		if (!attr || !attr->name || !attr->value) continue;
		if (!strcmp(attr->name, "uri")) {
			if (((VCalFolderItem *)item)->uri != NULL)
				g_free(((VCalFolderItem *)item)->uri);
			((VCalFolderItem *)item)->uri = g_strdup(attr->value);
		} 
	}
	if (((VCalFolderItem *)item)->uri == NULL) {
		/* give a path to inbox */
		g_free(item->path);
		item->path = g_strdup(".meetings");
	}
}

static XMLTag *vcal_item_get_xml(Folder *folder, FolderItem *item)
{
	static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
						 "draft", "queue", "trash"};
	static gchar *sort_key_str[] = {"none", "number", "size", "date",
					"from", "subject", "score", "label",
					"mark", "unread", "mime", "to", 
					"locked"};
	XMLTag *tag;
	gchar *value;

	tag = folder_item_get_xml(folder, item);

	if (((VCalFolderItem *)item)->uri)
		xml_tag_add_attr(tag, xml_attr_new("uri", ((VCalFolderItem *)item)->uri));

	return tag;
}

static gint vcal_rename_folder(Folder *folder, FolderItem *item,
			     const gchar *name)
{
	if (!name)
		return -1;
	g_free(item->name);
	item->name = g_strdup(name);
	return 0;
}

static void vcal_get_sort_type(Folder *folder, FolderSortKey *sort_key,
			       FolderSortType *sort_type)
{
	if (sort_key)
		*sort_key = SORT_BY_DATE;
}

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";

		/* Folder functions */
		vcal_class.new_folder = vcal_folder_new;
		vcal_class.destroy_folder = vcal_folder_destroy;
		vcal_class.set_xml = folder_set_xml;
		vcal_class.get_xml = folder_get_xml;
		vcal_class.item_set_xml = vcal_item_set_xml;
		vcal_class.item_get_xml = vcal_item_get_xml;
		vcal_class.scan_tree = vcal_scan_tree;
		vcal_class.create_tree = vcal_create_tree;
		vcal_class.get_sort_type = vcal_get_sort_type;

		/* FolderItem functions */
		vcal_class.item_new = vcal_item_new;
		vcal_class.item_destroy = vcal_item_destroy;
		vcal_class.item_get_path = vcal_item_get_path;
		vcal_class.create_folder = vcal_create_folder;
		vcal_class.remove_folder = vcal_remove_folder;
		vcal_class.rename_folder = vcal_rename_folder;
		vcal_class.scan_required = vcal_scan_required;
		vcal_class.get_num_list = vcal_get_num_list;
		vcal_class.set_batch = vcal_folder_set_batch;

		/* Message functions */
		vcal_class.get_msginfo = vcal_get_msginfo;
		vcal_class.fetch_msg = vcal_fetch_msg;
		vcal_class.add_msg = vcal_add_msg;
		vcal_class.copy_msg = NULL;
		vcal_class.remove_msg = vcal_remove_msg;
		vcal_class.change_flags = vcal_change_flags;
		vcal_class.subscribe = vcal_subscribe_uri;

		debug_print("registered class for real\n");
	}

	return &vcal_class;
}

static void vcal_folder_set_batch	(Folder		*folder,
					 FolderItem	*_item,
					 gboolean	 batch)
{
	VCalFolderItem *item = (VCalFolderItem *)_item;

	g_return_if_fail(item != NULL);
	
	if (item->batching == batch)
		return;
	
	if (batch) {
		item->batching = TRUE;
		debug_print("vcal switching to batch mode\n");
	} else {
		debug_print("vcal switching away from batch mode\n");
		/* ici */
		item->batching = FALSE;
		if (item->dirty)
			vcal_folder_export();
		item->dirty = FALSE;
	}
}

static Folder *vcal_folder_new(const gchar * name,
				  const gchar * path)
{
	VCalFolder *folder;		   
	
	debug_print("vcal_folder_new\n");
	folder = g_new0(VCalFolder, 1);
	FOLDER(folder)->klass = &vcal_class;
	folder_init(FOLDER(folder), name);

	return FOLDER(folder);
}

static void vcal_folder_destroy(Folder *_folder)
{
}

static FolderItem *vcal_item_new(Folder *folder)
{
	VCalFolderItem *item;
	item = g_new0(VCalFolderItem, 1);
	return (FolderItem *) item;

}

static void vcal_item_destroy(Folder *folder, FolderItem *_item)
{
	VCalFolderItem *item = (VCalFolderItem *)_item;
	g_return_if_fail(item != NULL);
	g_free(item);
}

static gchar *vcal_item_get_path(Folder *folder, FolderItem *item)
{
	VCalFolderItem *fitem = (VCalFolderItem *)item;
	if (fitem->uri == NULL)
		return g_strdup(vcal_manager_get_event_path());
	else {
		gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
					"vcalendar", G_DIR_SEPARATOR_S,
					item->path, NULL);
		return path;
	}
}

static gint vcal_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("scanning tree\n");
	vcal_create_tree(folder);

	return 0;
}

gboolean manual_update = TRUE;

static gint feed_fetch(FolderItem *fitem, MsgNumberList ** list, gboolean *old_uids_valid)
{
	VCalFolderItem *item = (VCalFolderItem *)fitem;
	MsgNumberList *msglist = NULL;
	icalcomponent *evt = NULL;
	icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
	gint num = 1;
	gint past_msg = -1, today_msg = -1, tomorrow_msg = -1, 
		thisweek_msg = -1, later_msg = -1;

	debug_print("fetching\n");

	if (!item->uri) {
		debug_print("no uri!\n");
		return -1;
	}

	update_subscription(item->uri, TRUE);

	*old_uids_valid = FALSE;
	*list = NULL;

	if (item->cal) {
		evt = icalcomponent_get_first_component(
			item->cal, ICAL_VEVENT_COMPONENT);
		if (evt == NULL) {
			evt = icalcomponent_get_first_component(
				item->cal, ICAL_VTODO_COMPONENT);
			if (evt != NULL)
				type = ICAL_VTODO_COMPONENT;
		}
	} else
		debug_print("no cal!\n");

	if (evt == NULL)
		debug_print("no event\n");

	if (item->numlist) {
		g_slist_free(item->numlist);
		item->numlist = NULL;
	}

	if (item->evtlist) {
		slist_free_icalfeeddata(item->evtlist);
		g_slist_free(item->evtlist);
		item->evtlist = NULL;
	}

	while (evt) {
		icalproperty *prop;
		icalproperty *rprop = icalcomponent_get_first_property(evt, ICAL_RRULE_PROPERTY);
		struct icalrecurrencetype recur;
        	struct icaltimetype next;
        	icalrecur_iterator* ritr = NULL;
		EventTime days;

		if (rprop) {
			icalproperty *rprop2;
			recur = icalproperty_get_rrule(rprop);
			rprop2 = icalproperty_new_rrule(recur);
			prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
			if (prop) {
        			ritr = icalrecur_iterator_new(recur, icalproperty_get_dtstart(prop));
				next = icalrecur_iterator_next(ritr); /* skip first one */				
			}
			
			rprop = rprop2;
		} 

		prop = icalcomponent_get_first_property(evt, ICAL_UID_PROPERTY);
		if (prop) {
			gchar *orig_uid = NULL;
			gchar *uid = g_strdup(icalproperty_get_uid(prop));
			IcalFeedData *data = icalfeeddata_new(evt, NULL);
			int i = 0;
			orig_uid = g_strdup(uid);

add_new:			
			item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
			item->evtlist = g_slist_append(item->evtlist, data);
			data = NULL;
			debug_print("add %d : %s\n", num, uid);
			g_free(uid);
			num++;
			prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
			if (prop) {
				struct icaltimetype itt = icalproperty_get_dtstart(prop);
				days = event_to_today(NULL, icaltime_as_timet(itt));
				if (days == EVENT_PAST && past_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_PAST_ID);
					past_msg = num++;
				} else if (days == EVENT_TODAY && today_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_TODAY_ID);
					today_msg = num++;
				} else if (days == EVENT_TOMORROW && tomorrow_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_TOMORROW_ID);
					tomorrow_msg = num++;
				} else if (days == EVENT_THISWEEK && thisweek_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_THISWEEK_ID);
					thisweek_msg = num++;
				} else if (days == EVENT_LATER && later_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_LATER_ID);
					later_msg = num++;
				}
			} else {
				if (past_msg == -1) {
					item->numlist = g_slist_append(item->numlist, GINT_TO_POINTER(num));
					data = icalfeeddata_new(NULL, EVENT_PAST_ID);
					past_msg = num++;
				}
			}
			if (data) {
				item->evtlist = g_slist_append(item->evtlist, data);
				data = NULL;
			}
			if (rprop) {
				struct icaldurationtype ical_dur;
				struct icaltimetype dtstart, dtend;
				evt = icalcomponent_new_clone(evt);
				prop = icalcomponent_get_first_property(evt, ICAL_RRULE_PROPERTY);
				if (prop)
					icalcomponent_remove_property(evt, prop);
				prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
				if (prop)
					dtstart = icalproperty_get_dtstart(prop);
				prop = icalcomponent_get_first_property(evt, ICAL_DTEND_PROPERTY);
				if (prop)
					dtend = icalproperty_get_dtend(prop);
				ical_dur = icaltime_subtract(dtend, dtstart);
				next = icalrecur_iterator_next(ritr); 
				if (!icaltime_is_null_time(next) &&
				    !icaltime_is_null_time(dtstart) && i < 100) {
					prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
					icalproperty_set_dtstart(prop, next);

					prop = icalcomponent_get_first_property(evt, ICAL_DTEND_PROPERTY);
					if (prop)
						icalproperty_set_dtend(prop, icaltime_add(next, ical_dur));

					prop = icalcomponent_get_first_property(evt, ICAL_UID_PROPERTY);
					uid = g_strdup_printf("%s-%d", orig_uid, i);
					icalproperty_set_uid(prop, uid);
					data = icalfeeddata_new(evt, NULL);
					i++;
					goto add_new;
				} 
			}
			g_free(orig_uid);
		} else {
			debug_print("no uid!\n");
		}
		if (rprop) {
			icalproperty_free(rprop);
		}
		if (ritr) {
			icalrecur_iterator_free(ritr);
			ritr = NULL;
		}
		evt = icalcomponent_get_next_component(
			item->cal, type);
	}
	*list = item->numlist ? g_slist_copy(item->numlist) : NULL;
	debug_print("return %d\n", num);
	return num;
}

#define VCAL_FOLDER_ADD_EVENT(event) \
{ \
 \
	debug_print("should add %d %s? %d %d\n", n_msg, event->uid, status, status == ICAL_PARTSTAT_ACCEPTED); \
	if (status == ICAL_PARTSTAT_ACCEPTED \
	||  status == ICAL_PARTSTAT_TENTATIVE) { \
		*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
		debug_print("add %d %s\n", n_msg, event->uid); \
		n_msg++; \
		days = event_to_today(event, 0); \
	 \
		if (days == EVENT_PAST && past_msg == -1) { \
			*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
			past_msg = n_msg++; \
			g_hash_table_insert(hash_uids, g_strdup_printf("%d", past_msg), g_strdup(EVENT_PAST_ID)); \
		} else if (days == EVENT_TODAY && today_msg == -1) { \
			*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
			today_msg = n_msg++; \
			g_hash_table_insert(hash_uids, g_strdup_printf("%d", today_msg), g_strdup(EVENT_TODAY_ID)); \
		} else if (days == EVENT_TOMORROW && tomorrow_msg == -1) { \
			*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
			tomorrow_msg = n_msg++; \
			g_hash_table_insert(hash_uids, g_strdup_printf("%d", tomorrow_msg), g_strdup(EVENT_TOMORROW_ID)); \
		} else if (days == EVENT_THISWEEK && thisweek_msg == -1) { \
			*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
			thisweek_msg = n_msg++; \
			g_hash_table_insert(hash_uids, g_strdup_printf("%d", thisweek_msg), g_strdup(EVENT_THISWEEK_ID)); \
		} else if (days == EVENT_LATER && later_msg == -1) { \
			*list = g_slist_append(*list, GINT_TO_POINTER(n_msg)); \
			later_msg = n_msg++; \
			g_hash_table_insert(hash_uids, g_strdup_printf("%d", later_msg), g_strdup(EVENT_LATER_ID)); \
		} \
	} \
}

static gint vcal_get_num_list(Folder *folder, FolderItem *item,
				 MsgNumberList ** list, gboolean *old_uids_valid)
{
	DIR *dp;
	struct dirent *d;
	int n_msg = 1;
	gchar *snmsg = NULL;
	gint past_msg = -1, today_msg = -1, tomorrow_msg = -1, 
		thisweek_msg = -1, later_msg = -1;

	debug_print(" num for %s\n", ((VCalFolderItem *)item)->uri);
	
	*old_uids_valid = FALSE;
	
	if (((VCalFolderItem *)item)->uri) 
		return feed_fetch(item, list, old_uids_valid);
	
	dp = opendir(vcal_manager_get_event_path());
	
	debug_print("get_num_list\n");
	if (!dp) {
		FILE_OP_ERROR(vcal_manager_get_event_path(), "opendir");
		return 0;
	}

	if (hash_uids != NULL)
		g_hash_table_destroy(hash_uids);
		
	hash_uids = g_hash_table_new_full(g_str_hash, g_str_equal,
					  g_free, g_free);
	
	while ((d = readdir(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")) 
			continue;

		snmsg = g_strdup_printf("%d",n_msg);
		g_hash_table_insert(hash_uids, snmsg, g_strdup(d->d_name));
		
		event = vcal_manager_load_event(d->d_name);
		if (!event)
			continue;
		if (event->rec_occurence) {
			vcal_manager_free_event(event);
			g_unlink(d->d_name);
			continue;
		}

		if (event && event->method != ICAL_METHOD_CANCEL) {
			EventTime days; \
			PrefsAccount *account = vcal_manager_get_account_from_event(event); \
			enum icalparameter_partstat status = \
				account ? vcal_manager_get_reply_for_attendee(event, account->address): ICAL_PARTSTAT_NEEDSACTION; \
			VCAL_FOLDER_ADD_EVENT(event);
			if (event->recur && strlen(event->recur)) {
        			struct icalrecurrencetype recur;
        			struct icaltimetype dtstart;
        			struct icaltimetype next;
        			icalrecur_iterator* ritr;
				time_t duration = (time_t) NULL;
				struct icaldurationtype ical_dur;
				int i = 0;

				debug_print("dumping recurring events from main event\n");
        			recur = icalrecurrencetype_from_string(event->recur);
				dtstart = icaltime_from_string(event->dtstart);

				duration = icaltime_as_timet(icaltime_from_string(event->dtend))
							    - icaltime_as_timet(icaltime_from_string(event->dtstart));

				ical_dur = icaldurationtype_from_int(duration);

        			ritr = icalrecur_iterator_new(recur, dtstart);

				next = icalrecur_iterator_next(ritr); /* skip first one */
				if (!icaltime_is_null_time(next))
					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;
					VCalEvent *nevent = NULL;
					gchar *uid = g_strdup_printf("%s-%d", event->uid, i);
					new_start = icaltime_as_ical_string(next);
					new_end = icaltime_as_ical_string(
							icaltime_add(next, ical_dur));
					debug_print("adding with start/end %s:%s\n", new_start, new_end);
					nevent = vcal_manager_new_event(uid, event->organizer, event->orgname, 
								event->summary, event->description, 
								new_start, new_end, NULL, 
								event->tzid, event->url, event->method, 
								event->sequence, event->type);
					vcal_manager_copy_attendees(event, nevent);
					nevent->rec_occurence = TRUE;
					vcal_manager_save_event(nevent, FALSE);
					snmsg = g_strdup_printf("%d",n_msg);
					g_hash_table_insert(hash_uids, snmsg, uid);
					VCAL_FOLDER_ADD_EVENT(nevent);
					vcal_manager_free_event(nevent);
					next = icalrecur_iterator_next(ritr);
					debug_print("next time is %snull\n", icaltime_is_null_time(next)?"":"not ");
					i++;
				}
				icalrecur_iterator_free(ritr);
			}
		}
		if (event)
			vcal_manager_free_event(event);


	}
	closedir(dp);
	vcal_folder_export();
	return g_slist_length(*list);
}

static MsgInfo *vcal_parse_msg(const gchar *file, FolderItem *item, int num)
{
	MsgInfo *msginfo = NULL;
	MsgFlags flags;

	debug_print("parse_msg\n");
	
	flags.perm_flags = 0;
	flags.tmp_flags = 0;
	msginfo = procheader_parse_file(file, flags, TRUE, TRUE);
	
	msginfo->msgnum = num;
	msginfo->folder = item;
	return msginfo;
}

static MsgInfo *vcal_get_msginfo(Folder * folder,
				    FolderItem * item, gint num)
{
	MsgInfo *msginfo = NULL;
	gchar *file;
	gchar *snum = NULL;

	debug_print("get_msginfo\n");
	
	g_return_val_if_fail(item != NULL, NULL);
	g_return_val_if_fail(num > 0, NULL);

	snum = g_strdup_printf("%d", num);

	file = vcal_fetch_msg(folder, item, num);

	if (!file) {
		g_free(snum);
		return NULL;
	}

	msginfo = vcal_parse_msg(file, item, num);

	if (msginfo) {
		msginfo->flags.perm_flags = 0;
		msginfo->flags.tmp_flags = 0;

		vcal_change_flags(NULL, NULL, msginfo, 0);

		debug_print("  adding %s\n", snum);
	}
	unlink(file);
	g_free(file);

	g_free(snum);

	debug_print("  got msginfo %p\n", msginfo);

	return msginfo;
}

static gchar *feed_fetch_item(FolderItem * fitem, gint num)
{
	gchar *filename = NULL;
	gchar *snum = NULL;
	gchar *uid = NULL;
	VCalFolderItem *item = (VCalFolderItem *)fitem;
	GSList *ncur, *ecur;
	int i = 1;
	IcalFeedData *data = NULL;
	icalproperty *prop = NULL;
	gchar *title = NULL;

	if (!item->numlist) {
		debug_print("numlist null\n");
		return NULL;
	}

	ncur = item->numlist;
	ecur = item->evtlist;
	
	while (i < num) {
		if (!ncur || !ecur) {
			debug_print("list short end (%d to %d) %d,%d\n", i, num, ncur!=NULL, ecur!=NULL);
			return NULL;
		}
		ncur = ncur->next;
		ecur = ecur->next;
		i++;
	}
	
	data = (IcalFeedData *)ecur->data;
	
	if (!data) {
		return NULL;
	}

	if (data->event)
		filename = vcal_manager_icalevent_dump(data->event, fitem->name, NULL);
	else if (data->pseudoevent_id) {
		filename = vcal_manager_dateevent_dump(data->pseudoevent_id, fitem);
		created_files = g_slist_prepend(created_files, g_strdup(filename));
	} else {
		debug_print("no event\n");
		return NULL;
	}

	debug_print("feed item dump to %s\n", filename);
	return filename;
}

static gchar *vcal_fetch_msg(Folder * folder, FolderItem * item,
				gint num)
{
	gchar *filename = NULL;
	gchar *snum = NULL;
	gchar *uid = NULL;
	
	debug_print(" fetch for %s %d\n", ((VCalFolderItem *)item)->uri, num);
	if (((VCalFolderItem *)item)->uri) 
		return feed_fetch_item(item, num);

	snum = g_strdup_printf("%d",num);
	uid = g_hash_table_lookup(hash_uids, snum);

	if (uid && 
	    (!strcmp(uid, EVENT_PAST_ID) ||
	     !strcmp(uid, EVENT_TODAY_ID) ||
	     !strcmp(uid, EVENT_TOMORROW_ID) ||
	     !strcmp(uid, EVENT_THISWEEK_ID) ||
	     !strcmp(uid, EVENT_LATER_ID))) {
		filename = vcal_manager_dateevent_dump(uid, item);
	} else if (uid) {
		VCalEvent *event = vcal_manager_load_event(uid);
		debug_print("getting %s\n", uid);
		debug_print("got event %p\n", event);
		if (event)
			filename = vcal_manager_event_dump(event, FALSE, TRUE, NULL, FALSE);
		debug_print("dumped to %s\n", filename);

		if (filename) {
			created_files = g_slist_prepend(created_files, g_strdup(filename));
		}

		vcal_manager_free_event(event);
	} 

	g_free(snum);
		
	return filename;
}

static gint vcal_add_msg(Folder *folder, FolderItem *_dest, const gchar *file, MsgFlags *flags)
{
	return 0;
}

static gint vcal_remove_msg(Folder *folder, FolderItem *_item, gint num)
{
	return 0;
}

static FolderItem *vcal_create_folder(Folder * folder,
					 FolderItem * parent,
					 const gchar * name)
{
	gchar *path = NULL;
	FolderItem *newitem = NULL;
	debug_print("creating new vcal folder\n");

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

	return newitem;
}

static gint vcal_create_tree(Folder *folder)
{
	FolderItem *rootitem, *inboxitem;
	GNode *rootnode, *inboxnode;

	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;
	}

	/* Add inbox folder */
	if (!folder->inbox) {
		inboxitem = folder_item_new(folder, "Meetings", ".meetings");
		inboxitem->folder = folder;
		inboxitem->stype = F_INBOX;
		inboxnode = g_node_new(inboxitem);
		inboxitem->node = inboxnode;
		folder->inbox = inboxitem;
		g_node_append(rootnode, inboxnode);	
	} else {
		g_free(folder->inbox->path);
		folder->inbox->path = g_strdup(".meetings");
	}

	debug_print("created new vcal tree\n");
	return 0;
}

static gint vcal_remove_folder(Folder *folder, FolderItem *fitem)
{
	VCalFolderItem *item = (VCalFolderItem *)fitem;
	if (!item->uri)
		return -1;
	else {
		if (item->feed)
			g_free(item->feed);
		if (item->uri)
			g_free(item->uri);
		item->feed = NULL;
		item->uri = NULL;
		folder_item_remove(fitem);
		return 0;
	}
}

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

static gint vcal_folder_lock_count = 0;

void vcal_folder_export(void)
{	
	if (vcal_folder_lock_count) /* blocked */
		return;
	vcal_folder_lock_count++;
	if (vcal_meeting_export_calendar(vcalprefs.export_path, 
			vcalprefs.export_user, 
			vcalprefs.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);
	}
	if (vcal_meeting_export_freebusy(vcalprefs.export_freebusy_path,
			vcalprefs.export_freebusy_user,
			vcalprefs.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);
	}
	vcal_folder_lock_count--;
}

static void vcal_remove_event (Folder *folder, MsgInfo *msginfo)
{
	MimeInfo *mime = procmime_scan_message(msginfo);
	gchar *uid = NULL;
	VCalFolderItem *item = (VCalFolderItem *)msginfo->folder;
	if (mime)
		mime = procmime_mimeinfo_next(mime);
	
	debug_print("next mime info:%s\n", mime?mime->subtype:"(nil)");
	if (mime && !strcmp(mime->subtype, "calendar")) {
		uid = vcalviewer_get_uid_from_mimeinfo(mime);
		if (uid) {
			gchar *file = vcal_manager_get_event_file(uid);
			g_free(uid);
			unlink(file);
			g_free(file);
		}
	}
	
	if (!item || !item->batching)
		vcal_folder_export();
	else if (item) {
		item->dirty = TRUE;
	}
}

static void vcal_change_flags(Folder *folder, FolderItem *_item, MsgInfo *msginfo, MsgPermFlags newflags)
{
	EventTime date;

	if (newflags & MSG_DELETED) {
		/* delete the stuff */
		msginfo->flags.perm_flags |= MSG_DELETED;
		vcal_remove_event(folder, msginfo);
		return;
	}

	/* accept the rest */
	msginfo->flags.perm_flags = newflags;

	/* but not color */
	msginfo->flags.perm_flags &= ~MSG_CLABEL_FLAG_MASK;
	
	date = event_to_today(NULL, msginfo->date_t);
	switch(date) {
	case EVENT_PAST:
		break;
	case EVENT_TODAY:
		msginfo->flags.perm_flags |= MSG_COLORLABEL_TO_FLAGS(2); /* Red */
		break;
	case EVENT_TOMORROW:
		/* msginfo->flags.perm_flags |= MSG_COLORLABEL_TO_FLAGS(1); /* Orange */
		break;
	case EVENT_THISWEEK:
		break;
	case EVENT_LATER:
		break;
	}
	if (msginfo->msgid) {
		if (!strcmp(msginfo->msgid, EVENT_TODAY_ID) ||
		    !strcmp(msginfo->msgid, EVENT_TOMORROW_ID))
		msginfo->flags.perm_flags |= MSG_MARKED;
	}
}

void vcal_folder_gtk_init(void)
{
	guint i, n_entries;

	vcal_fill_popup_menu_labels();
	n_entries = sizeof(vcal_popup_entries) /
		sizeof(vcal_popup_entries[0]);
	for (i = 0; i < n_entries; i++)
		vcal_popup.entries = g_slist_append(vcal_popup.entries, &vcal_popup_entries[i]);

	folderview_register_popup(&vcal_popup);
}

void vcal_folder_gtk_done(void)
{
	GSList *cur = created_files;
	while (cur) {
		gchar *file = (gchar *)cur->data;
		cur = cur->next;
		if (!file)			
			continue;
		debug_print("removing %s\n", file);
		unlink(file);
		g_free(file);
	}
	g_slist_free(created_files);
	folderview_unregister_popup(&vcal_popup);
}

static void set_sensitivity(GtkItemFactory *factory, FolderItem *fitem)
{
	VCalFolderItem *item = (VCalFolderItem *)fitem;

#define SET_SENS(name, sens) \
	menu_set_sensitive(factory, name, sens)

	SET_SENS(_("/New meeting..."), item->uri == NULL);
	SET_SENS(_("/Export calendar..."), TRUE);
	SET_SENS(_("/Subscribe to webCal..."), item->uri == NULL);
	SET_SENS(_("/Unsubscribe..."), item->uri != NULL);
	SET_SENS(_("/Rename..."), folder_item_parent(fitem) != NULL);
	SET_SENS(_("/Update subscriptions"), TRUE);
	
#undef SET_SENS
}

static void new_meeting_cb(FolderView *folderview, guint action, GtkWidget *widget)
{
	debug_print("new_meeting_cb\n");
	vcal_meeting_create(NULL);
}

GSList * vcal_folder_get_waiting_events(void)
{
	DIR *dp;
	struct dirent *d;
	GSList *list = NULL;

	dp = opendir(vcal_manager_get_event_path());

	if (!dp) {
		FILE_OP_ERROR(vcal_manager_get_event_path(), "opendir");
		return 0;
	}

	while ((d = readdir(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")) 
			continue;

		event = vcal_manager_load_event(d->d_name);

		if (event && event->method != ICAL_METHOD_CANCEL) {
			PrefsAccount *account = vcal_manager_get_account_from_event(event);
			enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
			status = account ? vcal_manager_get_reply_for_attendee(event, account->address): ICAL_PARTSTAT_NEEDSACTION;

			if (status == ICAL_PARTSTAT_ACCEPTED
			||  status == ICAL_PARTSTAT_TENTATIVE) {
				list = g_slist_append(list, event);
			} else {
				vcal_manager_free_event(event);
			}
		} else {
			vcal_manager_free_event(event);
		}
	}

	closedir(dp);
		
	return list;
}

typedef struct _get_webcal_data {
	GSList *list;
} GetWebcalData;

static gboolean get_webcal_events_func(GNode *node, gpointer user_data)
{
	FolderItem *item = node->data;
	GetWebcalData *data = user_data;
	gboolean dummy = FALSE;
	GSList *list = NULL, *cur = NULL;

	feed_fetch(item, &list, &dummy);

	
	for (cur = ((VCalFolderItem *)item)->evtlist; cur; cur = cur->next) {
		IcalFeedData *fdata = (IcalFeedData *)cur->data;
		if (fdata->event)
			data->list = g_slist_append(data->list, fdata->event);
	}
	return FALSE;
}

GSList * vcal_folder_get_webcal_events(void)
{
	GetWebcalData *data = g_new0(GetWebcalData, 1);
	Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
	GSList *list = NULL;
	g_node_traverse(folder->node, G_PRE_ORDER,
			G_TRAVERSE_ALL, -1, get_webcal_events_func, data);

	list = data->list;
	g_free(data);

	return list;
}

gchar* get_item_event_list_for_date(FolderItem *item, EventTime date)
{
	GSList *strs = NULL;
	GSList *cur;
	gchar *result = NULL;
	gchar *datestr = NULL;

	if (((VCalFolderItem *)item)->uri) {
		for (cur = ((VCalFolderItem *)item)->evtlist; cur; cur = cur->next) {
			IcalFeedData *fdata = (IcalFeedData *)cur->data;
			icalcomponent *event;
			icalproperty *prop;
			struct icaltimetype itt;
			gchar *str = NULL;
			gchar *summary = NULL;
			EventTime days;
			if (!fdata->event)
				continue;
			prop = icalcomponent_get_first_property((icalcomponent *)fdata->event, ICAL_DTSTART_PROPERTY);
			
			if (!prop)
				continue;
			itt = icalproperty_get_dtstart(prop);
			days = event_to_today(NULL, icaltime_as_timet(itt));
			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), 
						conv_get_locale_charset_str(), CS_UTF_8);
				else
					summary = g_strdup(icalproperty_get_summary(prop));
			} else
				summary = g_strdup("-");

			strs = g_slist_append(strs, summary);
		}
	} else {
		GSList *evtlist = vcal_folder_get_waiting_events();
		for (cur = evtlist; cur; cur = cur->next) {
			VCalEvent *event = (VCalEvent *)cur->data;
			EventTime days;
			days = event_to_today(event, 0);
			gchar *summary = NULL;
			if (days == date) {
				summary = g_strdup(event->summary);
				strs = g_slist_append(strs, summary);
			}
			vcal_manager_free_event(event);
		}
	}
	
	switch(date) {
	case EVENT_PAST:
		datestr=_("in the past");
		break;
	case EVENT_TODAY:
		datestr=_("today");
		break;
	case EVENT_TOMORROW:
		datestr=_("tomorrow");
		break;
	case EVENT_THISWEEK:
		datestr=_("this week");
		break;
	case EVENT_LATER:
		datestr=_("later");
		break;
	}
	
	result = g_strdup_printf(_("\nThese are the events planned %s:\n"),
			datestr);
	
	for (cur = strs; cur; cur = cur->next) {
		int e_len = strlen(result);
		int n_len = strlen((gchar *)cur->data);
		if (e_len) {
			result = g_realloc(result, e_len+n_len+4);
			*(result+e_len) = '\n';
			strcpy(result+e_len+1, "- ");
			strcpy(result+e_len+3, (gchar *)cur->data);
		} else {
			result = g_realloc(result, e_len+n_len+3);
			strcpy(result+e_len, "- ");
			strcpy(result+e_len+2, (gchar *)cur->data);
		}
	}
	slist_free_strings(strs);
	g_slist_free(strs);
	return result;
}

static void export_cal_cb(FolderView *folderview, guint action, GtkWidget *widget)
{
	vcal_meeting_export_calendar(NULL, NULL, NULL, FALSE);
}

struct CBuf {
	gchar *str;
};

static int 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];

	memcpy(tmpbuf, buf, size*nmemb);
	tmpbuf[size*nmemb] = '\0';

	if (buffer->str) {
		tmp = g_strconcat(buffer->str, tmpbuf, NULL);
		g_free(buffer->str);
		buffer->str = tmp;
	} else {
		buffer->str = g_strdup(tmpbuf);
	}

	return size*nmemb;
}

void *url_read_thread(void *data)
{
	thread_data *td = (thread_data *)data;
	CURLcode res;
	CURL *curl_ctx = NULL;
	long response_code;
	struct CBuf buffer = { NULL };
	gchar *t_url = (gchar *)td->url;

	while (*t_url == ' ')
		t_url++;
	if (strchr(t_url, ' '))
		*(strchr(t_url, ' ')) = '\0';

#ifdef USE_PTHREAD
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
#endif
	
	curl_ctx = curl_easy_init();
	
	curl_easy_setopt(curl_ctx, CURLOPT_URL, t_url);
	curl_easy_setopt(curl_ctx, CURLOPT_WRITEFUNCTION, curl_recv);
	curl_easy_setopt(curl_ctx, CURLOPT_WRITEDATA, &buffer);
	curl_easy_setopt(curl_ctx, CURLOPT_TIMEOUT, prefs_common.io_timeout_secs);
#if LIBCURL_VERSION_NUM >= 0x070a00
	curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYHOST, 0);
#endif
	curl_easy_setopt(curl_ctx, CURLOPT_USERAGENT, 
		"Claws Mail vCalendar plugin "
		"(" PLUGINS_URI ")");
	curl_easy_setopt(curl_ctx, CURLOPT_FOLLOWLOCATION, 1);
	res = curl_easy_perform(curl_ctx);
	
	if (res != 0) {
		debug_print("res %d %s\n", res, curl_easy_strerror(res));
		td->error = g_strdup(curl_easy_strerror(res));
		
		if(res == CURLE_OPERATION_TIMEOUTED)
			log_error(LOG_PROTOCOL, _("Timeout (%d seconds) connecting to %s\n"),
				prefs_common.io_timeout_secs, t_url);
	}

	curl_easy_getinfo(curl_ctx, CURLINFO_RESPONSE_CODE, &response_code);
	if( response_code >= 400 && response_code < 500 ) {
		debug_print("VCalendar: got %d\n", response_code);
		switch(response_code) {
			case 401: 
				td->error = g_strdup(_("401 (Authorisation required)"));
				break;
			case 403:
				td->error = g_strdup(_("403 (Unauthorised)"));
				break;
			case 404:
				td->error = g_strdup(_("404 (Not found)"));
				break;
			default:
				td->error = g_strdup_printf(_("Error %d"), response_code);
				break;
		}
	}
	curl_easy_cleanup(curl_ctx);
	if (buffer.str) {
		td->result = g_strdup(buffer.str);
		g_free(buffer.str);
	}

	td->done = TRUE; /* let the caller thread join() */
	return GINT_TO_POINTER(0);
}

gchar *vcal_curl_read(const char *url, gboolean verbose, 
	void (*callback)(const gchar *url, gchar *data, gboolean verbose, gchar *error))
{
	gchar *result;
	thread_data *td;
#ifdef USE_PTHREAD
	pthread_t pt;
#endif
	gchar *msg;
	void *res;
	time_t start_time;
	gboolean killed;
	gchar *error = NULL;
	result = NULL;
	td = g_new0(thread_data, 1);
	msg = NULL;
	res = NULL;
	killed = FALSE;
	
	td->url  = url;
	td->result  = NULL;
	td->done = FALSE;
	
	msg = g_strdup_printf(_("Fetching '%s'..."), url);
	
	STATUSBAR_PUSH(mainwindow_get_mainwindow(), msg);
	
	g_free(msg);

#if (defined USE_PTHREAD && ((defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3))) || !defined __GLIBC__))
	if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, 
			url_read_thread, td) != 0) {
		url_read_thread(td);	
	}
	while (!td->done)  {
 		claws_do_idle();
	}
 
	pthread_join(pt, &res);
#else
	url_read_thread(td);
#endif
	
	result = td->result;
	error = td->error;
	g_free(td);
	
	STATUSBAR_POP(mainwindow_get_mainwindow());

	if (callback) {
		callback(url, result, verbose, error);
		return NULL;
	} else {
		if (error)
			g_free(error);
		return result;
	}
}

gboolean vcal_curl_put(gchar *url, FILE *fp, gint filesize, const gchar *user, const gchar *pass)
{
	gboolean res = TRUE;
	CURL *curl_ctx = curl_easy_init();
	int response_code = 0;
	gchar *t_url = url;
	gchar *userpwd = NULL;

	struct curl_slist * headers = curl_slist_append(NULL, 
		"Content-Type: text/calendar; charset=\"utf-8\"" );

	while (*t_url == ' ')
		t_url++;
	if (strchr(t_url, ' '))
		*(strchr(t_url, ' ')) = '\0';

	if (user && pass && *user && *pass) {
		userpwd = g_strdup_printf("%s:%s",user,pass);
		curl_easy_setopt(curl_ctx, CURLOPT_USERPWD, userpwd);
	}
	curl_easy_setopt(curl_ctx, CURLOPT_URL, t_url);
	curl_easy_setopt(curl_ctx, CURLOPT_UPLOAD, 1);
	curl_easy_setopt(curl_ctx, CURLOPT_READDATA, fp);
	curl_easy_setopt(curl_ctx, CURLOPT_HTTPHEADER, headers);
#if LIBCURL_VERSION_NUM >= 0x070a00
	curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
	curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYHOST, 0);
#endif
	curl_easy_setopt(curl_ctx, CURLOPT_USERAGENT, 
		"Claws Mail vCalendar plugin "
		"(http://www.claws-mail.org/plugins.php)");
	curl_easy_setopt(curl_ctx, CURLOPT_INFILESIZE, filesize);
	res = curl_easy_perform(curl_ctx);
	g_free(userpwd);

	if (res != 0) {
		debug_print("res %d %s\n", res, curl_easy_strerror(res));
	} else {
		res = TRUE;
	}

	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 %d\n", response_code);
		res = FALSE;
	}
	curl_easy_cleanup(curl_ctx);
	curl_slist_free_all(headers);
	return res;
}

static gboolean folder_item_find_func(GNode *node, gpointer data)
{
	FolderItem *item = node->data;
	gpointer *d = data;
	const gchar *uri = d[0];

	if (!uri || !((VCalFolderItem *)item)->uri
	||  strcmp(uri, ((VCalFolderItem *)item)->uri))
		return FALSE;

	d[1] = item;

	return TRUE;
}

static FolderItem *get_folder_item_for_uri(const gchar *uri)
{
	FolderItem *tmp = NULL;
	Folder *root = folder_find_from_name ("vCalendar", vcal_folder_get_class());
	gpointer d[2];
	
	if (root == NULL)
		return NULL;
	
	d[0] = (gpointer)uri;
	d[1] = NULL;
	g_node_traverse(root->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
			folder_item_find_func, d);
	return d[1];
}

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;
}

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());
	FolderItem *item = NULL;
	icalcomponent *cal = NULL;
	
	if (root == NULL) {
		g_warning("can't get root folder\n");
		g_free(feed);
		if (error)
			g_free(error);
		return;
	}

	if (feed == NULL) {
		if (verbose && manual_update) {
			alertpanel_error(_("Could not retrieve the Webcal URL:\n%s\n%s"),
					uri, error ? error:_("Unknown error"));
		} else  {
			log_error(LOG_PROTOCOL, _("Could not retrieve the Webcal URL:\n%s\n%s\n"),
					uri, error ? error:_("Unknown error"));
		}
		main_window_cursor_normal(mainwindow_get_mainwindow());
		g_free(feed);
		if (error)
			g_free(error);
		return;
	}
	if (strncmp(feed, "BEGIN:VCALENDAR", strlen("BEGIN:VCALENDAR"))) {
		if (verbose && manual_update) {
			alertpanel_error(_("This URL does not look like a WebCal URL:\n%s\n%s"),
					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"));
		}
		g_free(feed);
		main_window_cursor_normal(mainwindow_get_mainwindow());
		if (error)
			g_free(error);
		return;
	}
	
	if (error)
		g_free(error);
	item = get_folder_item_for_uri(uri);
	if (item == NULL) {
		gchar *title = feed_get_title(feed);
		if (title == NULL) {
			if (strstr(uri, "://"))
				title = g_strdup(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);
		debug_print("item done %s\n", title);
		((VCalFolderItem *)item)->uri = g_strdup(uri);
		((VCalFolderItem *)item)->feed = feed;
		g_free(title);
	} else {
		if (((VCalFolderItem *)item)->feed)
			g_free(((VCalFolderItem *)item)->feed);

		((VCalFolderItem *)item)->feed = feed;
		/* if title differs, update it */
	}
	cal = icalparser_parse_string(feed);
	
	if (((VCalFolderItem *)item)->cal)
		icalcomponent_free(((VCalFolderItem *)item)->cal);

	((VCalFolderItem *)item)->cal = cal;
	
	main_window_cursor_normal(mainwindow_get_mainwindow());
}

static void update_subscription(const gchar *uri, gboolean verbose)
{
	if (prefs_common.work_offline) {
		if (!verbose || 
		!inc_offline_should_override(TRUE,
		   _("Claws Mail needs network access in order "
		     "to update the Webcal feed.")))
			return;
	}
	
	main_window_cursor_wait(mainwindow_get_mainwindow());
	vcal_curl_read(uri, verbose, update_subscription_finish);
}

static void check_subs_cb(FolderView *folderview, guint action, GtkWidget *widget)
{
	Folder *root = folder_find_from_name ("vCalendar", vcal_folder_get_class());

	if (prefs_common.work_offline && 
	    !inc_offline_should_override(TRUE,
		     _("Claws Mail needs network access in order "
		     "to update the subscription.")))
		return;

	folderview_check_new(root);
}

static void subscribe_cal_cb(FolderView *folderview, guint action, GtkWidget *widget)
{
	gchar *uri = NULL;
	gchar *tmp = NULL;

	tmp = input_dialog(_("Subscribe to WebCal"), _("Enter the WebCal URL:"), NULL);
	if (tmp == NULL)
		return;
	
	if (!strncmp(tmp, "http://", 7)) {
		uri = tmp;
	} else if (!strncmp(tmp, "https://", 8)) {
		uri = tmp;
	} else if (!strncmp(tmp, "webcal://", 9)) {
		uri = g_strconcat("http://", tmp+9, NULL);
		g_free(tmp);
	} else {
		alertpanel_error(_("Could not parse the URL."));
		g_free(tmp);
		return;
	}
	debug_print("uri %s\n", uri);
	
	update_subscription(uri, TRUE);	
	folder_write_list();
	g_free(uri);
}

static void unsubscribe_cal_cb(FolderView *folderview, guint action, GtkWidget *widget)
{
	GtkCTree *ctree = GTK_CTREE(folderview->ctree);
	FolderItem *item;
	gchar *message;
	AlertValue avalue;
	gchar *old_path;
	gchar *old_id;

	if (!folderview->selected) return;

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

	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);
	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_ctree_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'."), item->name);
		g_free(old_id);
		return;
	}

	folder_write_list();

	prefs_filtering_delete_path(old_id);
	g_free(old_id);
}

gboolean vcal_subscribe_uri(Folder *folder, const gchar *uri)
{
	gchar *tmp = NULL;
	if (folder->klass != vcal_folder_get_class())
		return FALSE;

	if (!strncmp(uri, "webcal://", 9)) {
		tmp = g_strconcat("http://", uri+9, NULL);
	} else {
		return FALSE;
	}
	debug_print("uri %s\n", tmp);
	
	update_subscription(tmp, FALSE);
	folder_write_list();
	return TRUE;
}

static void rename_cb(FolderView *folderview, guint action,
			     GtkWidget *widget)
{
	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, 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();
}


syntax highlighted by Code2HTML, v. 0.9.1