/* * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 1999-2007 Colin Leroy 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" #include "claws-features.h" #endif #include #include #include #include #include #ifdef USE_PTHREAD #include #endif #include #include "vcalendar.h" #include "vcal_folder.h" #include "vcal_manager.h" #include "vcal_meeting_gtk.h" #include "vcal_prefs.h" #include "xml.h" #include "xmlprops.h" #include "prefs.h" #include "prefs_common.h" #include "prefs_account.h" #include "alertpanel.h" #include "account.h" #include "codeconv.h" #include #include "folder.h" #include "quoted-printable.h" #include "utils.h" #ifdef G_OS_WIN32 #define getuid() 0 #endif Answer *answer_new(const gchar *attendee, const gchar *name, enum icalparameter_partstat ans, enum icalparameter_cutype cutype) { Answer *answer = g_new0(Answer, 1); answer->attendee = g_strdup(attendee); answer->name = g_strdup(name); if (!answer->name) answer->name = g_strdup(""); if (!answer->attendee) answer->attendee = g_strdup(""); answer->answer = ans; answer->cutype = cutype; return answer; } static void answer_free(Answer *answer) { g_free(answer->attendee); g_free(answer->name); g_free(answer); } static GSList *answer_find(VCalEvent *event, Answer *answer) { GSList *cur = event->answers; while (cur && cur->data) { Answer *b = (Answer *)cur->data; if (!strcasecmp(b->attendee, answer->attendee)) return cur; cur = cur->next; } return NULL; } void vcal_manager_copy_attendees(VCalEvent *src, VCalEvent *dest) { GSList *cur = src->answers; while (cur && cur->data) { Answer *a = (Answer *)cur->data; Answer *b = answer_new(a->attendee, a->name, a->answer, a->cutype); dest->answers = g_slist_prepend(dest->answers, b); cur = cur->next; } dest->answers = g_slist_reverse(dest->answers); } gchar *vcal_manager_answer_get_text(enum icalparameter_partstat ans) { static gchar *replies[5]={ N_("accepted"), N_("tentatively accepted"), N_("declined"), N_("did not answer"), N_("unknown") }; switch (ans) { case ICAL_PARTSTAT_ACCEPTED: return _(replies[0]); break; case ICAL_PARTSTAT_DECLINED: return _(replies[2]); break; case ICAL_PARTSTAT_TENTATIVE: return _(replies[1]); break; case ICAL_PARTSTAT_NEEDSACTION: return _(replies[3]); case ICAL_PARTSTAT_DELEGATED: case ICAL_PARTSTAT_COMPLETED: case ICAL_PARTSTAT_X: case ICAL_PARTSTAT_INPROCESS: case ICAL_PARTSTAT_NONE: return _(replies[4]); break; } return NULL; } gchar *vcal_manager_cutype_get_text(enum icalparameter_cutype type) { static gchar *replies[5]={ N_("individual"), N_("group"), N_("resource"), N_("room"), N_("unknown") }; switch (type) { case ICAL_CUTYPE_INDIVIDUAL: return _(replies[0]); break; case ICAL_CUTYPE_GROUP: return _(replies[1]); break; case ICAL_CUTYPE_RESOURCE: return _(replies[2]); break; case ICAL_CUTYPE_ROOM: return _(replies[3]); default: return _(replies[4]); break; } return NULL; } static GSList *answer_remove(VCalEvent *event, Answer *answer) { GSList *cur = answer_find(event, answer); if (cur) { Answer *b = (Answer *)cur->data; event->answers = g_slist_remove(event->answers, b); answer_free(b); } return event->answers; } static GSList *answer_add(VCalEvent *event, Answer *answer) { event->answers = g_slist_append(event->answers, answer); return event->answers; } GSList *vcal_manager_get_answers_emails(VCalEvent *event) { GSList *new = NULL; GSList *cur = event->answers; while (cur && cur->data) { Answer *b = (Answer *)cur->data; new = g_slist_prepend(new, b->attendee); cur = cur->next; } new = g_slist_reverse(new); return new; } enum icalparameter_partstat vcal_manager_get_reply_for_attendee(VCalEvent *event, const gchar *att) { Answer *a = answer_new(att, NULL, 0, 0); GSList *ans = answer_find(event, a); enum icalparameter_partstat res = 0; if (ans) { Answer *b = (Answer *)ans->data; res = b->answer; } answer_free(a); return res; } gchar *vcal_manager_get_cutype_text_for_attendee(VCalEvent *event, const gchar *att) { enum icalparameter_cutype status = vcal_manager_get_cutype_for_attendee(event, att); gchar *res = NULL; if (status != 0) res = g_strdup(vcal_manager_cutype_get_text(status)); return res; } enum icalparameter_partstat vcal_manager_get_cutype_for_attendee(VCalEvent *event, const gchar *att) { Answer *a = answer_new(att, NULL, 0, 0); GSList *ans = answer_find(event, a); enum icalparameter_cutype res = 0; if (ans) { Answer *b = (Answer *)ans->data; res = b->cutype; } answer_free(a); return res; } gchar *vcal_manager_get_reply_text_for_attendee(VCalEvent *event, const gchar *att) { enum icalparameter_partstat status = vcal_manager_get_reply_for_attendee(event, att); gchar *res = NULL; if (status != 0) res = g_strdup(vcal_manager_answer_get_text(status)); return res; } gchar *vcal_manager_get_attendee_name(VCalEvent *event, const gchar *att) { Answer *a = answer_new(att, NULL, 0, 0); GSList *ans = answer_find(event, a); gchar *res = NULL; if (ans) { Answer *b = (Answer *)ans->data; if (b->name) res = g_strdup(b->name); } answer_free(a); return res; } void vcal_manager_event_print(VCalEvent *event) { GSList *list = event->answers; printf( "event->uid\t\t%s\n" "event->organizer\t\t%s\n" "event->start\t\t%s\n" "event->end\t\t%s\n" "event->location\t\t%s\n" "event->summary\t\t%s\n" "event->description\t%s\n" "event->url\t%s\n" "event->dtstart\t\t%s\n" "event->dtend\t\t%s\n" "event->recur\t\t%s\n" "event->tzid\t\t%s\n" "event->method\t\t%d\n" "event->sequence\t\t%d\n", event->uid, event->organizer, event->start, event->end, event->location, event->summary, event->description, event->url, event->dtstart, event->dtend, event->recur, event->tzid, event->method, event->sequence); while (list && list->data) { Answer *a = (Answer *)list->data; printf(" ans: %s %s, %s\n", a->name, a->attendee, vcal_manager_answer_get_text(a->answer)); list = list->next; } } static gchar *write_headers(PrefsAccount *account, VCalEvent *event, gboolean short_headers, gboolean is_reply, gboolean is_pseudo_event); gchar *vcal_manager_event_dump(VCalEvent *event, gboolean is_reply, gboolean is_pseudo_event, icalcomponent *use_calendar, gboolean modif) { gchar *organizer = g_strdup_printf("MAILTO:%s", event->organizer); PrefsAccount *account = vcal_manager_get_account_from_event(event); gchar *attendee = NULL; gchar *body, *headers; gchar *tmpfile = NULL; icalcomponent *calendar, *ievent, *timezone, *tzc; icalproperty *attprop; icalproperty *orgprop; enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION; gchar *sanitized_uid = g_strdup(event->uid); subst_for_filename(sanitized_uid); tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(), G_DIR_SEPARATOR, getuid(), sanitized_uid); g_free(sanitized_uid); if (!account) { g_free(organizer); g_free(tmpfile); debug_print("no account found\n"); return NULL; } attendee = g_strdup_printf("MAILTO:%s", account->address); if (vcal_manager_get_reply_for_attendee(event, account->address) != 0) status = vcal_manager_get_reply_for_attendee(event, account->address); tzset(); if (use_calendar != NULL) { calendar = use_calendar; g_free(tmpfile); tmpfile = NULL; } else calendar = icalcomponent_vanew( ICAL_VCALENDAR_COMPONENT, icalproperty_new_version("2.0"), icalproperty_new_prodid( "-//Claws Mail//NONSGML Claws Mail Calendar//EN"), icalproperty_new_calscale("GREGORIAN"), icalproperty_new_method(is_reply ? ICAL_METHOD_REPLY:event->method), 0 ); if (!calendar) { g_warning ("can't generate calendar"); g_free(organizer); g_free(tmpfile); g_free(attendee); return NULL; } orgprop = icalproperty_new_organizer(organizer); timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT); icalcomponent_add_property(timezone, icalproperty_new_tzid("UTC")); /* free */ tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT); icalcomponent_add_property(tzc, icalproperty_new_dtstart( icaltime_from_string("19700101T000000"))); icalcomponent_add_property(tzc, icalproperty_new_tzoffsetfrom(0.0)); icalcomponent_add_property(tzc, icalproperty_new_tzoffsetto(0.0)); icalcomponent_add_property(tzc, icalproperty_new_tzname("Greenwich meridian time")); icalcomponent_add_component(timezone, tzc); ievent = icalcomponent_vanew( ICAL_VEVENT_COMPONENT, 0); if (!ievent) { g_warning ("can't generate event"); g_free(organizer); g_free(tmpfile); g_free(attendee); return NULL; } icalcomponent_add_property(ievent, icalproperty_new_uid(event->uid)); icalcomponent_add_property(ievent, icalproperty_vanew_dtstamp(icaltime_from_timet(time(NULL), TRUE), 0)); icalcomponent_add_property(ievent, icalproperty_vanew_dtstart((icaltime_from_string(event->dtstart)), 0)); icalcomponent_add_property(ievent, icalproperty_vanew_dtend((icaltime_from_string(event->dtend)), 0)); if (event->recur && *(event->recur)) { icalcomponent_add_property(ievent, icalproperty_vanew_rrule((icalrecurrencetype_from_string(event->recur)), 0)); } icalcomponent_add_property(ievent, icalproperty_new_description(event->description)); icalcomponent_add_property(ievent, icalproperty_new_summary(event->summary)); icalcomponent_add_property(ievent, icalproperty_new_sequence(modif && !is_reply ? event->sequence + 1 : event->sequence)); icalcomponent_add_property(ievent, icalproperty_new_class("PUBLIC")); icalcomponent_add_property(ievent, icalproperty_new_transp("OPAQUE")); if (event->location && *event->location) icalcomponent_add_property(ievent, icalproperty_new_location(event->location)); else icalcomponent_add_property(ievent, icalproperty_new_location("")); icalcomponent_add_property(ievent, icalproperty_new_status(ICAL_STATUS_CONFIRMED)); icalcomponent_add_property(ievent, icalproperty_vanew_created(icaltime_from_timet(time(NULL), TRUE), 0)); icalcomponent_add_property(ievent, icalproperty_vanew_lastmodified(icaltime_from_timet(time(NULL), TRUE), 0)); icalcomponent_add_property(ievent, orgprop); if (!icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT)) icalcomponent_add_component(calendar, timezone); else icalcomponent_free(timezone); icalcomponent_add_component(calendar, ievent); if (event->url && *(event->url)) { attprop = icalproperty_new_url(event->url); icalcomponent_add_property(ievent, attprop); } if (is_reply) { /* dump only this attendee */ attprop = icalproperty_vanew_attendee( attendee, icalparameter_new_role( ICAL_ROLE_REQPARTICIPANT), icalparameter_new_rsvp(ICAL_RSVP_TRUE), icalparameter_new_partstat(status), 0 ); icalcomponent_add_property(ievent, attprop); } else { /* dump all attendees */ GSList *cur = event->answers; while (cur && cur->data) { Answer *a = (Answer *)cur->data; if (a->cutype == 0) a->cutype = ICAL_CUTYPE_INDIVIDUAL; if (a->answer == 0) a->answer = ICAL_PARTSTAT_NEEDSACTION; attprop = icalproperty_vanew_attendee( a->attendee, icalparameter_new_role( ICAL_ROLE_REQPARTICIPANT), icalparameter_new_rsvp(ICAL_RSVP_TRUE), icalparameter_new_cutype(a->cutype), icalparameter_new_partstat(a->answer), 0 ); icalcomponent_add_property(ievent, attprop); cur = cur->next; } } if (use_calendar) { g_free(organizer); g_free(tmpfile); g_free(attendee); return NULL; } headers = write_headers(account, event, is_pseudo_event, is_reply, is_pseudo_event); if (!headers) { g_warning("can't get headers"); g_free(organizer); g_free(tmpfile); g_free(attendee); return NULL; } body = g_strdup_printf("%s" "\n" "%s", headers, icalcomponent_as_ical_string(calendar)); if (str_write_to_file(body, tmpfile) < 0) { g_free(tmpfile); tmpfile = NULL; } chmod(tmpfile, S_IRUSR|S_IWUSR); g_free(body); g_free(headers); icalcomponent_free(calendar); g_free(attendee); g_free(organizer); tzset(); return tmpfile; } static void get_rfc822_date_from_time_t(gchar *buf, gint len, time_t t) { struct tm *lt; gchar day[4], mon[4]; gint dd, hh, mm, ss, yyyy; gchar buft1[512]; struct tm buft2; #ifndef G_OS_WIN32 lt = localtime_r(&t, &buft2); #else if (t < 0) t = 1; lt = localtime(&t); #endif sscanf(asctime_r(lt, buft1), "%3s %3s %d %d:%d:%d %d\n", day, mon, &dd, &hh, &mm, &ss, &yyyy); g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s", day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t)); } static gchar *write_headers_date(const gchar *uid) { gchar subject[512]; gchar *t_subject; gchar date[128]; time_t t; struct tm lt; struct tm buft; memset(subject, 0, sizeof(subject)); memset(date, 0, sizeof(date)); if (!strcmp(uid, EVENT_PAST_ID)) { t = 1; t_subject = _("Past"); } else if (!strcmp(uid, EVENT_TODAY_ID)) { t = time(NULL); t_subject = _("Today"); } else if (!strcmp(uid, EVENT_TOMORROW_ID)) { t = time(NULL) + 86400; t_subject = _("Tomorrow"); } else if (!strcmp(uid, EVENT_THISWEEK_ID)) { t = time(NULL) + (86400*2); t_subject = _("This week"); } else if (!strcmp(uid, EVENT_LATER_ID)) { t = time(NULL) + (86400*7); t_subject = _("Later"); } else { g_warning("unknown spec date"); return NULL; } #ifndef G_OS_WIN32 lt = *localtime_r(&t, &buft); #else if (t < 0) t = 1; lt = *localtime(&t); #endif lt.tm_hour = lt.tm_min = lt.tm_sec = 0; t = mktime(<); get_rfc822_date_from_time_t(date, sizeof(date), t); conv_encode_header(subject, 511, t_subject, strlen("Subject: "), FALSE); return g_strdup_printf("From: -\n" "To: -\n" "Subject: %s\n" "Date: %s\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=\"UTF-8\";\n" "Content-Transfer-Encoding: quoted-printable\n" "Message-ID: <%s>\n", subject, date, uid); } gchar *vcal_manager_dateevent_dump(const gchar *uid, FolderItem *item) { gchar *sanitized_uid = NULL; gchar *headers = NULL; gchar *lines, *body, *tmpfile; EventTime date; sanitized_uid = g_strdup(uid); subst_for_filename(sanitized_uid); tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(), G_DIR_SEPARATOR, getuid(), sanitized_uid); g_free(sanitized_uid); headers = write_headers_date(uid); if (!headers) { g_warning("can't get headers"); g_free(tmpfile); return NULL; } if (!strcmp(uid, EVENT_PAST_ID)) date = EVENT_PAST; else if (!strcmp(uid, EVENT_TODAY_ID)) date = EVENT_TODAY; else if (!strcmp(uid, EVENT_TOMORROW_ID)) date = EVENT_TOMORROW; else if (!strcmp(uid, EVENT_THISWEEK_ID)) date = EVENT_THISWEEK; else if (!strcmp(uid, EVENT_LATER_ID)) date = EVENT_LATER; else date = EVENT_PAST; lines = get_item_event_list_for_date(item, date); body = g_strdup_printf("%s" "\n" "%s", headers, lines); g_free(lines); if (str_write_to_file(body, tmpfile) < 0) { g_free(tmpfile); tmpfile = NULL; } else chmod(tmpfile, S_IRUSR|S_IWUSR); g_free(body); g_free(headers); return tmpfile; } static gchar *write_headers_ical(PrefsAccount *account, icalcomponent *ievent, gchar *orga); gchar *vcal_manager_icalevent_dump(icalcomponent *event, gchar *orga, icalcomponent *use_calendar) { PrefsAccount *account = account_get_cur_account(); gchar *body, *headers, *qpbody; gchar **lines = NULL; gchar *tmpfile = NULL; icalcomponent *calendar; icalproperty *prop; icalcomponent *ievent = NULL; int i = 0; ievent = icalcomponent_new_clone(event); prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY); if (prop) { gchar *sanitized_uid = g_strdup(icalproperty_get_uid(prop)); subst_for_filename(sanitized_uid); tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(), G_DIR_SEPARATOR, getuid(), sanitized_uid); g_free(sanitized_uid); icalproperty_free(prop); } else { tmpfile = g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(), G_DIR_SEPARATOR, getuid(), ievent); } if (!account) { g_free(tmpfile); icalcomponent_free(ievent); return NULL; } tzset(); if (use_calendar != NULL) { calendar = use_calendar; g_free(tmpfile); tmpfile = NULL; } else calendar = icalcomponent_vanew( ICAL_VCALENDAR_COMPONENT, icalproperty_new_version("2.0"), icalproperty_new_prodid( "-//Claws Mail//NONSGML Claws Mail Calendar//EN"), icalproperty_new_calscale("GREGORIAN"), icalproperty_new_method(ICAL_METHOD_PUBLISH), 0 ); if (!calendar) { g_warning ("can't generate calendar"); g_free(tmpfile); icalcomponent_free(ievent); return NULL; } icalcomponent_add_component(calendar, ievent); if (use_calendar) return NULL; headers = write_headers_ical(account, ievent, orga); if (!headers) { g_warning("can't get headers"); g_free(tmpfile); icalcomponent_free(calendar); return NULL; } lines = g_strsplit(icalcomponent_as_ical_string(calendar), "\n", 0); qpbody = g_strdup(""); /* encode to quoted-printable */ while (lines[i]) { gchar buf[256]; gint e_len = strlen(qpbody), n_len = 0; gchar *outline = conv_codeset_strdup(lines[i], CS_UTF_8, conv_get_outgoing_charset_str()); qp_encode_line(buf, (guchar *)outline); n_len = strlen(buf); qpbody = g_realloc(qpbody, e_len + n_len + 1); strcpy(qpbody+e_len, buf); *(qpbody+n_len+e_len) = '\0'; g_free(outline); i++; } body = g_strdup_printf("%s" "\n" "%s", headers, qpbody); if (str_write_to_file(body, tmpfile) < 0) { g_free(tmpfile); tmpfile = NULL; } else chmod(tmpfile, S_IRUSR|S_IWUSR); g_strfreev(lines); g_free(body); g_free(qpbody); g_free(headers); icalcomponent_free(calendar); return tmpfile; } VCalEvent * vcal_manager_new_event (const gchar *uid, const gchar *organizer, const gchar *orgname, const gchar *location, const gchar *summary, const gchar *description, const gchar *dtstart, const gchar *dtend, const gchar *recur, const gchar *tzid, const gchar *url, enum icalproperty_method method, gint sequence, enum icalcomponent_kind type) { VCalEvent *event = g_new0(VCalEvent, 1); event->uid = g_strdup(uid?uid:""); event->organizer = g_strdup(organizer?organizer:""); event->orgname = g_strdup(orgname?orgname:""); if (dtend && *(dtend)) { time_t tmp = icaltime_as_timet((icaltime_from_string(dtend))); gchar buft[512]; tzset(); event->end = g_strdup(ctime_r(&tmp, buft)); } if (dtstart && *(dtstart)) { time_t tmp = icaltime_as_timet((icaltime_from_string(dtstart))); gchar buft[512]; tzset(); event->start = g_strdup(ctime_r(&tmp, buft)); } event->dtstart = g_strdup(dtstart?dtstart:""); event->dtend = g_strdup(dtend?dtend:""); event->recur = g_strdup(recur?recur:""); event->location = g_strdup(location?location:""); event->summary = g_strdup(summary?summary:""); event->description = g_strdup(description?description:""); event->url = g_strdup(url?url:""); event->tzid = g_strdup(tzid?tzid:""); event->method = method; event->sequence = sequence; event->type = type; event->rec_occurence = FALSE; while (strchr(event->summary, '\n')) *(strchr(event->summary, '\n')) = ' '; return event; } void vcal_manager_free_event (VCalEvent *event) { GSList *cur; if (!event) return; g_free(event->uid); g_free(event->organizer); g_free(event->orgname); g_free(event->start); g_free(event->end); g_free(event->location); g_free(event->summary); g_free(event->dtstart); g_free(event->dtend); g_free(event->recur); g_free(event->tzid); g_free(event->description); g_free(event->url); for (cur = event->answers; cur; cur = cur->next) { answer_free((Answer *)cur->data); } g_slist_free(event->answers); g_free(event); } gchar *vcal_manager_get_event_path(void) { static gchar *event_path = NULL; if (!event_path) event_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "vcalendar", NULL); return event_path; } gchar *vcal_manager_get_event_file(const gchar *uid) { gchar *tmp = g_strdup(uid); gchar *res = NULL; subst_for_filename(tmp); res = g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S, tmp, NULL); g_free(tmp); return res; } PrefsAccount *vcal_manager_get_account_from_event(VCalEvent *event) { GSList *list = vcal_manager_get_answers_emails(event); GSList *cur = list; /* find an attendee corresponding to one of our accounts */ while (cur && cur->data) { gchar *email = (gchar *)cur->data; if (account_find_from_address(email, FALSE)) { g_slist_free(list); return account_find_from_address(email, FALSE); } cur = cur->next; } g_slist_free(list); return NULL; } void vcal_manager_save_event (VCalEvent *event, gboolean export_after) { XMLTag *tag = NULL; XMLNode *xmlnode = NULL; GNode *rootnode = NULL; PrefFile *pfile; gchar *path = NULL; GSList *answers = event->answers; gchar *tmp = NULL; gint tmp_method = event->method; tag = xml_tag_new("event"); xml_tag_add_attr(tag, xml_attr_new("organizer", event->organizer)); xml_tag_add_attr(tag, xml_attr_new("orgname", event->orgname)); xml_tag_add_attr(tag, xml_attr_new("location", event->location)); xml_tag_add_attr(tag, xml_attr_new("summary", event->summary)); xml_tag_add_attr(tag, xml_attr_new("description", event->description)); xml_tag_add_attr(tag, xml_attr_new("url", event->url)); xml_tag_add_attr(tag, xml_attr_new("dtstart", event->dtstart)); xml_tag_add_attr(tag, xml_attr_new("dtend", event->dtend)); xml_tag_add_attr(tag, xml_attr_new("recur", event->recur)); xml_tag_add_attr(tag, xml_attr_new("tzid", event->tzid)); /* updating answers saves events, don't save them with reply type */ if (tmp_method == ICAL_METHOD_REPLY) tmp_method = ICAL_METHOD_REQUEST; tmp = g_strdup_printf("%d", tmp_method); xml_tag_add_attr(tag, xml_attr_new("method", tmp)); g_free(tmp); tmp = g_strdup_printf("%d", event->sequence); xml_tag_add_attr(tag, xml_attr_new("sequence", tmp)); g_free(tmp); tmp = g_strdup_printf("%d", event->type); xml_tag_add_attr(tag, xml_attr_new("type", tmp)); g_free(tmp); tmp = g_strdup_printf("%lld", (long long)event->postponed); xml_tag_add_attr(tag, xml_attr_new("postponed", tmp)); g_free(tmp); tmp = g_strdup_printf("%d", event->rec_occurence); xml_tag_add_attr(tag, xml_attr_new("rec_occurence", tmp)); g_free(tmp); xmlnode = xml_node_new(tag, NULL); rootnode = g_node_new(xmlnode); while (answers && answers->data) { XMLNode *ansxmlnode = NULL; GNode *ansnode = NULL; XMLTag *anstag = xml_tag_new("answer"); Answer *a = (Answer *)answers->data; xml_tag_add_attr(anstag, xml_attr_new("attendee", a->attendee)); xml_tag_add_attr(anstag, xml_attr_new("name", a->name?a->name:"")); tmp = g_strdup_printf("%d", a->answer); xml_tag_add_attr(anstag, xml_attr_new("answer", tmp)); g_free(tmp); tmp = g_strdup_printf("%d", a->cutype); xml_tag_add_attr(anstag, xml_attr_new("cutype", tmp)); g_free(tmp); ansxmlnode = xml_node_new(anstag, NULL); ansnode = g_node_new(ansxmlnode); g_node_append(rootnode, ansnode); answers = answers->next; } path = vcal_manager_get_event_file(event->uid); if ((pfile = prefs_write_open(path)) == NULL) { gchar *dir_path = vcal_manager_get_event_path(); if (!is_dir_exist(dir_path) && make_dir(vcal_manager_get_event_path()) != 0) { g_free(dir_path); g_free(path); return; } g_free(dir_path); if ((pfile = prefs_write_open(path)) == NULL) { g_free(path); return; } } g_free(path); xml_file_put_xml_decl(pfile->fp); xml_write_tree(rootnode, pfile->fp); xml_free_tree(rootnode); if (prefs_file_close(pfile) < 0) { g_warning("failed to write event."); return; } if (export_after) vcal_folder_export(NULL); } static VCalEvent *event_get_from_xml (const gchar *uid, GNode *node) { XMLNode *xmlnode; GList *list; gchar *org = NULL, *location = NULL, *summary = NULL, *orgname = NULL; gchar *dtstart = NULL, *dtend = NULL, *tzid = NULL; gchar *description = NULL, *url = NULL, *recur = NULL; VCalEvent *event = NULL; enum icalproperty_method method = ICAL_METHOD_REQUEST; enum icalcomponent_kind type = ICAL_VEVENT_COMPONENT; gint sequence = 0, rec_occurence = 0; time_t postponed = (time_t)0; g_return_val_if_fail(node->data != NULL, NULL); xmlnode = node->data; if (strcmp2(xmlnode->tag->tag, "event") != 0) { g_warning("tag name != \"event\""); return NULL; } list = xmlnode->tag->attr; for (; list != NULL; list = list->next) { XMLAttr *attr = list->data; if (!attr || !attr->name || !attr->value) continue; if (!strcmp(attr->name, "organizer")) org = g_strdup(attr->value); if (!strcmp(attr->name, "orgname")) orgname = g_strdup(attr->value); if (!strcmp(attr->name, "location")) location = g_strdup(attr->value); if (!strcmp(attr->name, "summary")) summary = g_strdup(attr->value); if (!strcmp(attr->name, "description")) description = g_strdup(attr->value); if (!strcmp(attr->name, "url")) url = g_strdup(attr->value); if (!strcmp(attr->name, "dtstart")) dtstart = g_strdup(attr->value); if (!strcmp(attr->name, "dtend")) dtend = g_strdup(attr->value); if (!strcmp(attr->name, "recur")) recur = g_strdup(attr->value); if (!strcmp(attr->name, "tzid")) tzid = g_strdup(attr->value); if (!strcmp(attr->name, "type")) type = atoi(attr->value); if (!strcmp(attr->name, "method")) method = atoi(attr->value); if (!strcmp(attr->name, "sequence")) sequence = atoi(attr->value); if (!strcmp(attr->name, "postponed")) postponed = atoi(attr->value); if (!strcmp(attr->name, "rec_occurence")) rec_occurence = atoi(attr->value); } event = vcal_manager_new_event(uid, org, orgname, location, summary, description, dtstart, dtend, recur, tzid, url, method, sequence, type); event->postponed = postponed; event->rec_occurence = rec_occurence; g_free(org); g_free(orgname); g_free(location); g_free(summary); g_free(description); g_free(url); g_free(dtstart); g_free(dtend); g_free(recur); g_free(tzid); node = node->children; while (node != NULL) { gchar *attendee = NULL; gchar *name = NULL; enum icalparameter_partstat answer = ICAL_PARTSTAT_NEEDSACTION; enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL; xmlnode = node->data; if (strcmp2(xmlnode->tag->tag, "answer") != 0) { g_warning("tag name != \"answer\""); return event; } list = xmlnode->tag->attr; for (; list != NULL; list = list->next) { XMLAttr *attr = list->data; if (!attr || !attr->name || !attr->value) continue; if (!strcmp(attr->name, "attendee")) attendee = g_strdup(attr->value); if (!strcmp(attr->name, "name")) name = g_strdup(attr->value); if (!strcmp(attr->name, "answer")) answer = atoi(attr->value); if (!strcmp(attr->name, "cutype")) cutype = atoi(attr->value); } event->answers = g_slist_prepend(event->answers, answer_new(attendee, name, answer, cutype)); g_free(attendee); g_free(name); node = node->next; } event->answers = g_slist_reverse(event->answers); return event; } VCalEvent *vcal_manager_load_event (const gchar *uid) { GNode *node; gchar *path = NULL; VCalEvent *event = NULL; path = vcal_manager_get_event_file(uid); if (!is_file_exist(path)) { g_free(path); return NULL; } node = xml_parse_file(path); g_free(path); if (!node) { g_warning("no node"); return NULL; } event = event_get_from_xml(uid, node); /* vcal_manager_event_print(event); */ xml_free_tree(node); if (!event) return NULL; while (strchr(event->summary, '\n')) *(strchr(event->summary, '\n')) = ' '; return event; } void vcal_manager_update_answer (VCalEvent *event, const gchar *attendee, const gchar *name, enum icalparameter_partstat ans, enum icalparameter_cutype cutype) { Answer *answer = NULL; GSList *existing = NULL; Answer *existing_a = NULL; if (!ans) return; answer = answer_new(attendee, name, ans, cutype); existing = answer_find(event, answer); if (existing) { existing_a = (Answer *)existing->data; if (!answer->name && existing_a->name) answer->name = g_strdup(existing_a->name); if (!answer->cutype && existing_a->cutype) answer->cutype = existing_a->cutype; answer_remove(event, answer); } answer_add(event, answer); vcal_manager_save_event(event, FALSE); } static gchar *write_headers(PrefsAccount *account, VCalEvent *event, gboolean short_headers, gboolean is_reply, gboolean is_pseudo_display) { gchar *subject = NULL; gchar date[128]; gchar *save_folder = NULL; gchar *result = NULL; gchar *queue_headers = NULL; gchar *method_str = NULL; gchar *attendees = NULL; enum icalparameter_partstat status; gchar *prefix = NULL; gchar enc_subject[512], enc_from[512], *from = NULL; gchar msgid[128]; gchar *calmsgid = NULL; cm_return_val_if_fail(account != NULL, NULL); memset(date, 0, sizeof(date)); if (is_pseudo_display) { struct icaltimetype itt = (icaltime_from_string(event->dtstart)); time_t t = icaltime_as_timet(itt); get_rfc822_date_from_time_t(date, sizeof(date), t); } else { get_rfc822_date(date, sizeof(date)); } if (account_get_special_folder(account, F_OUTBOX)) { save_folder = folder_item_get_identifier(account_get_special_folder (account, F_OUTBOX)); } if (!is_reply) { GSList *cur = event->answers; while (cur && cur->data) { gchar *tmp = NULL; Answer *a = (Answer *)cur->data; if (strcasecmp(a->attendee, event->organizer)) { if (attendees) { tmp = g_strdup_printf("%s>,\n <%s", attendees, a->attendee); g_free(attendees); attendees = tmp; } else { attendees = g_strdup_printf("%s", a->attendee); } } cur = cur->next; } } if (!short_headers) { queue_headers = g_strdup_printf("S:%s\n" "SSV:%s\n" "R:<%s>\n" "MAID:%d\n" "%s%s%s" "X-Claws-End-Special-Headers: 1\n", account->address, account->smtp_server, is_reply ? event->organizer:attendees, account->account_id, save_folder?"SCF:":"", save_folder?save_folder:"", save_folder?"\n":""); } else { queue_headers = g_strdup(""); } prefix = ""; if (is_reply) { method_str = "REPLY"; status = vcal_manager_get_reply_for_attendee(event, account->address); if (status == ICAL_PARTSTAT_ACCEPTED) prefix = _("Accepted: "); else if (status == ICAL_PARTSTAT_DECLINED) prefix = _("Declined: "); else if (status == ICAL_PARTSTAT_TENTATIVE) prefix = _("Tentatively Accepted: "); else prefix = "Re: "; } else if (event->method == ICAL_METHOD_PUBLISH) { method_str = "PUBLISH"; } else if (event->method == ICAL_METHOD_CANCEL) { method_str = "CANCEL"; } else { method_str = "REQUEST"; } subject = g_strdup_printf("%s%s", prefix, event->summary); conv_encode_header_full(enc_subject, sizeof(enc_subject), subject, strlen("Subject: "), FALSE, conv_get_outgoing_charset_str()); from = is_reply?account->name:(event->orgname?event->orgname:""); conv_encode_header_full(enc_from, sizeof(enc_from), from, strlen("From: "), TRUE, conv_get_outgoing_charset_str()); if (is_pseudo_display && event->uid) { calmsgid = g_strdup_printf("Message-ID: <%s>\n",event->uid); } else { calmsgid = g_strdup(""); } if (account && account->set_domain && account->domain) { g_snprintf(msgid, sizeof(msgid), "%s", account->domain); } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) { g_snprintf(msgid, sizeof(msgid), "%s", strchr(account->address, '@') ? strchr(account->address, '@')+1 : account->address); } else { g_snprintf(msgid, sizeof(msgid), "%s", ""); } generate_msgid(msgid, sizeof(msgid), account->address); result = g_strdup_printf("%s" "From: %s <%s>\n" "To: <%s>\n" "Subject: %s\n" "Date: %s\n" "MIME-Version: 1.0\n" "Content-Type: text/calendar; method=%s; charset=\"%s\"\n" "Content-Transfer-Encoding: 8bit\n" "%s" "%s: <%s>\n", queue_headers, enc_from, is_reply ? account->address:event->organizer, is_reply ? event->organizer:(attendees?attendees:event->organizer), enc_subject, date, method_str, CS_UTF_8, calmsgid, is_pseudo_display? "In-Reply-To":"Message-ID", is_pseudo_display? event_to_today_str(event, 0):msgid); g_free(calmsgid); g_free(subject); g_free(save_folder); g_free(queue_headers); g_free(attendees); return result; } static gchar *write_headers_ical(PrefsAccount *account, icalcomponent *ievent, gchar *orga) { gchar subject[512]; gchar date[128]; gchar *result = NULL; gchar *method_str = NULL; gchar *summary = NULL; gchar *organizer = NULL; gchar *orgname = NULL; icalproperty *prop = NULL; gchar *calmsgid = NULL; time_t t = (time_t)0; memset(subject, 0, sizeof(subject)); memset(date, 0, sizeof(date)); prop = icalcomponent_get_first_property(ievent, ICAL_SUMMARY_PROPERTY); if (prop) { summary = g_strdup(icalproperty_get_summary(prop)); icalproperty_free(prop); } else { summary = g_strdup(""); } while (strchr(summary, '\n')) *(strchr(summary, '\n')) = ' '; prop = icalcomponent_get_first_property(ievent, ICAL_ORGANIZER_PROPERTY); if (prop) { organizer = g_strdup(icalproperty_get_organizer(prop)); if (icalproperty_get_parameter_as_string(prop, "CN") != NULL) orgname = g_strdup(icalproperty_get_parameter_as_string(prop, "CN")); icalproperty_free(prop); } else { organizer = orga? g_strdup(orga):g_strdup(""); } prop = icalcomponent_get_first_property(ievent, ICAL_DTSTART_PROPERTY); if (prop) { t = icaltime_as_timet(icalproperty_get_dtstart(prop)); get_rfc822_date_from_time_t(date, sizeof(date), t); } else { get_rfc822_date(date, sizeof(date)); } conv_encode_header(subject, 511, summary, strlen("Subject: "), FALSE); method_str = "PUBLISH"; prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY); if (prop) { calmsgid = g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop)); icalproperty_free(prop); } else { calmsgid = g_strdup(""); } result = g_strdup_printf("From: %s <%s>\n" "To: <%s>\n" "Subject: %s%s\n" "Date: %s\n" "MIME-Version: 1.0\n" "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n" "Content-Transfer-Encoding: quoted-printable\n" "%s" "In-Reply-To: <%s>\n", orgname?orgname:"", !strncmp(organizer, "MAILTO:", 7) ? organizer+7 : organizer, account->address, "", subject, date, method_str, conv_get_outgoing_charset_str(), calmsgid, event_to_today_str(NULL, t)); g_free(calmsgid); g_free(orgname); g_free(organizer); g_free(summary); return result; } static gboolean vcal_manager_send (PrefsAccount *account, VCalEvent *event, gboolean is_reply) { gchar *tmpfile = NULL; gint msgnum; FolderItem *folderitem; gchar *msgpath = NULL; Folder *folder = NULL; tmpfile = vcal_manager_event_dump(event, is_reply, FALSE, NULL, TRUE); if (!tmpfile) return FALSE; folderitem = account_get_special_folder(account, F_QUEUE); if (!folderitem) { g_warning("can't find queue folder for %s", account->address); g_unlink(tmpfile); g_free(tmpfile); return FALSE; } folder_item_scan(folderitem); if ((msgnum = folder_item_add_msg(folderitem, tmpfile, NULL, TRUE)) < 0) { g_warning("can't queue the message"); g_unlink(tmpfile); g_free(tmpfile); return FALSE; } msgpath = folder_item_fetch_msg(folderitem, msgnum); if (!prefs_common_get_prefs()->work_offline) { gchar *err = NULL; gboolean queued_removed = FALSE; gint val = procmsg_send_message_queue_with_lock(msgpath, &err, folderitem, msgnum, &queued_removed); if (val == 0) { if (!queued_removed) folder_item_remove_msg(folderitem, msgnum); folder_item_scan(folderitem); } else if (err) { alertpanel_error_log("%s", err); g_free(err); } } g_unlink(tmpfile); g_free(tmpfile); g_free(msgpath); folder = folder_find_from_name ("vCalendar", vcal_folder_get_class()); if (folder) folder_item_scan(folder->inbox); vcalviewer_reload(folder->inbox); return TRUE; } gboolean vcal_manager_reply (PrefsAccount *account, VCalEvent *event) { return vcal_manager_send(account, event, TRUE); } gboolean vcal_manager_request (PrefsAccount *account, VCalEvent *event) { return vcal_manager_send(account, event, FALSE); } EventTime event_to_today(VCalEvent *event, time_t t) { struct tm evtstart, today; time_t evtstart_t, today_t; struct icaltimetype itt; struct tm buft; tzset(); today_t = time(NULL); if (event) { itt = icaltime_from_string(event->dtstart); evtstart_t = icaltime_as_timet(itt); } else { evtstart_t = t; } #ifndef G_OS_WIN32 today = *localtime_r(&today_t, &buft); localtime_r(&evtstart_t, &evtstart); #else if (today_t < 0) today_t = 1; if (evtstart_t < 0) evtstart_t = 1; today = *localtime(&today_t); evtstart = *localtime(&evtstart_t); #endif if (today.tm_year == evtstart.tm_year) { int days = evtstart.tm_yday - today.tm_yday; if (days < 0) { return EVENT_PAST; } else if (days == 0) { return EVENT_TODAY; } else if (days == 1) { return EVENT_TOMORROW; } else if (days > 1 && days < 7) { return EVENT_THISWEEK; } else { return EVENT_LATER; } } else if (today.tm_year > evtstart.tm_year) { return EVENT_PAST; } else if (today.tm_year == evtstart.tm_year - 1) { int days = ((365 - today.tm_yday) + evtstart.tm_yday); if (days == 0) { return EVENT_TODAY; } else if (days == 1) { return EVENT_TOMORROW; } else if (days > 1 && days < 7) { return EVENT_THISWEEK; } else { return EVENT_LATER; } } else return EVENT_LATER; } const gchar *event_to_today_str(VCalEvent *event, time_t t) { EventTime days = event_to_today(event, t); switch(days) { case EVENT_PAST: return EVENT_PAST_ID; case EVENT_TODAY: return EVENT_TODAY_ID; case EVENT_TOMORROW: return EVENT_TOMORROW_ID; case EVENT_THISWEEK: return EVENT_THISWEEK_ID; case EVENT_LATER: return EVENT_LATER_ID; } return NULL; }