/*
* 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