2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Colin Leroy <colin@colino.net> and
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "claws-features.h"
28 #include <glib/gi18n.h>
30 #include <sys/types.h>
36 #include "vcalendar.h"
37 #include "vcal_folder.h"
38 #include "vcal_manager.h"
39 #include "vcal_meeting_gtk.h"
40 #include "vcal_prefs.h"
44 #include "prefs_common.h"
45 #include "prefs_account.h"
46 #include "alertpanel.h"
51 #include "quoted-printable.h"
58 Answer *answer_new(const gchar *attendee,
60 enum icalparameter_partstat ans,
61 enum icalparameter_cutype cutype)
63 Answer *answer = g_new0(Answer, 1);
64 answer->attendee = g_strdup(attendee);
65 answer->name = g_strdup(name);
67 answer->name = g_strdup("");
68 if (!answer->attendee)
69 answer->attendee = g_strdup("");
71 answer->cutype = cutype;
75 static void answer_free(Answer *answer)
77 g_free(answer->attendee);
82 static GSList *answer_find(VCalEvent *event, Answer *answer)
84 GSList *cur = event->answers;
85 while (cur && cur->data) {
86 Answer *b = (Answer *)cur->data;
87 if (!strcasecmp(b->attendee, answer->attendee))
94 void vcal_manager_copy_attendees(VCalEvent *src, VCalEvent *dest)
96 GSList *cur = src->answers;
97 while (cur && cur->data) {
98 Answer *a = (Answer *)cur->data;
99 Answer *b = answer_new(a->attendee, a->name, a->answer, a->cutype);
100 dest->answers = g_slist_prepend(dest->answers, b);
103 dest->answers = g_slist_reverse(dest->answers);
106 gchar *vcal_manager_answer_get_text(enum icalparameter_partstat ans)
108 static gchar *replies[5]={
110 N_("tentatively accepted"),
112 N_("did not answer"),
117 case ICAL_PARTSTAT_ACCEPTED:
118 return _(replies[0]);
120 case ICAL_PARTSTAT_DECLINED:
121 return _(replies[2]);
123 case ICAL_PARTSTAT_TENTATIVE:
124 return _(replies[1]);
126 case ICAL_PARTSTAT_NEEDSACTION:
127 return _(replies[3]);
128 case ICAL_PARTSTAT_DELEGATED:
129 case ICAL_PARTSTAT_COMPLETED:
130 case ICAL_PARTSTAT_X:
131 case ICAL_PARTSTAT_INPROCESS:
132 case ICAL_PARTSTAT_NONE:
133 return _(replies[4]);
139 gchar *vcal_manager_cutype_get_text(enum icalparameter_cutype type)
141 static gchar *replies[5]={
150 case ICAL_CUTYPE_INDIVIDUAL:
151 return _(replies[0]);
153 case ICAL_CUTYPE_GROUP:
154 return _(replies[1]);
156 case ICAL_CUTYPE_RESOURCE:
157 return _(replies[2]);
159 case ICAL_CUTYPE_ROOM:
160 return _(replies[3]);
162 return _(replies[4]);
168 static GSList *answer_remove(VCalEvent *event, Answer *answer)
170 GSList *cur = answer_find(event, answer);
172 Answer *b = (Answer *)cur->data;
173 event->answers = g_slist_remove(event->answers, b);
176 return event->answers;
179 static GSList *answer_add(VCalEvent *event, Answer *answer)
181 event->answers = g_slist_append(event->answers, answer);
182 return event->answers;
185 GSList *vcal_manager_get_answers_emails(VCalEvent *event)
188 GSList *cur = event->answers;
189 while (cur && cur->data) {
190 Answer *b = (Answer *)cur->data;
191 new = g_slist_prepend(new, b->attendee);
194 new = g_slist_reverse(new);
198 enum icalparameter_partstat vcal_manager_get_reply_for_attendee(VCalEvent *event, const gchar *att)
200 Answer *a = answer_new(att, NULL, 0, 0);
201 GSList *ans = answer_find(event, a);
202 enum icalparameter_partstat res = 0;
204 Answer *b = (Answer *)ans->data;
211 gchar *vcal_manager_get_cutype_text_for_attendee(VCalEvent *event, const gchar *att)
213 enum icalparameter_cutype status = vcal_manager_get_cutype_for_attendee(event, att);
216 res = g_strdup(vcal_manager_cutype_get_text(status));
221 enum icalparameter_partstat vcal_manager_get_cutype_for_attendee(VCalEvent *event, const gchar *att)
223 Answer *a = answer_new(att, NULL, 0, 0);
224 GSList *ans = answer_find(event, a);
225 enum icalparameter_cutype res = 0;
227 Answer *b = (Answer *)ans->data;
234 gchar *vcal_manager_get_reply_text_for_attendee(VCalEvent *event, const gchar *att)
236 enum icalparameter_partstat status = vcal_manager_get_reply_for_attendee(event, att);
239 res = g_strdup(vcal_manager_answer_get_text(status));
244 gchar *vcal_manager_get_attendee_name(VCalEvent *event, const gchar *att)
246 Answer *a = answer_new(att, NULL, 0, 0);
247 GSList *ans = answer_find(event, a);
250 Answer *b = (Answer *)ans->data;
252 res = g_strdup(b->name);
258 void vcal_manager_event_print(VCalEvent *event)
260 GSList *list = event->answers;
261 printf( "event->uid\t\t%s\n"
262 "event->organizer\t\t%s\n"
263 "event->start\t\t%s\n"
265 "event->location\t\t%s\n"
266 "event->summary\t\t%s\n"
267 "event->description\t%s\n"
269 "event->dtstart\t\t%s\n"
270 "event->dtend\t\t%s\n"
271 "event->recur\t\t%s\n"
272 "event->tzid\t\t%s\n"
273 "event->method\t\t%d\n"
274 "event->sequence\t\t%d\n",
289 while (list && list->data) {
290 Answer *a = (Answer *)list->data;
291 printf(" ans: %s %s, %s\n", a->name, a->attendee, vcal_manager_answer_get_text(a->answer));
297 static gchar *write_headers(PrefsAccount *account,
299 gboolean short_headers,
301 gboolean is_pseudo_event);
303 gchar *vcal_manager_event_dump(VCalEvent *event, gboolean is_reply, gboolean is_pseudo_event,
304 icalcomponent *use_calendar, gboolean modif)
306 gchar *organizer = g_strdup_printf("MAILTO:%s", event->organizer);
307 PrefsAccount *account = vcal_manager_get_account_from_event(event);
308 gchar *attendee = NULL;
309 gchar *body, *headers;
310 gchar *tmpfile = NULL;
311 icalcomponent *calendar, *ievent, *timezone, *tzc;
312 icalproperty *attprop;
313 icalproperty *orgprop;
314 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
315 gchar *sanitized_uid = g_strdup(event->uid);
317 subst_for_filename(sanitized_uid);
319 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
320 G_DIR_SEPARATOR, getuid(), sanitized_uid);
321 g_free(sanitized_uid);
326 debug_print("no account found\n");
330 attendee = g_strdup_printf("MAILTO:%s", account->address);
332 if (vcal_manager_get_reply_for_attendee(event, account->address) != 0)
333 status = vcal_manager_get_reply_for_attendee(event, account->address);
337 if (use_calendar != NULL) {
338 calendar = use_calendar;
344 ICAL_VCALENDAR_COMPONENT,
345 icalproperty_new_version("2.0"),
346 icalproperty_new_prodid(
347 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
348 icalproperty_new_calscale("GREGORIAN"),
349 icalproperty_new_method(is_reply ? ICAL_METHOD_REPLY:event->method),
354 g_warning ("can't generate calendar");
361 orgprop = icalproperty_new_organizer(organizer);
363 timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
365 icalcomponent_add_property(timezone,
366 icalproperty_new_tzid("UTC")); /* free */
368 tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
369 icalcomponent_add_property(tzc,
370 icalproperty_new_dtstart(
371 icaltime_from_string("19700101T000000")));
372 icalcomponent_add_property(tzc,
373 icalproperty_new_tzoffsetfrom(0.0));
374 icalcomponent_add_property(tzc,
375 icalproperty_new_tzoffsetto(0.0));
376 icalcomponent_add_property(tzc,
377 icalproperty_new_tzname("Greenwich meridian time"));
379 icalcomponent_add_component(timezone, tzc);
383 ICAL_VEVENT_COMPONENT, (void*)0);
386 g_warning ("can't generate event");
393 icalcomponent_add_property(ievent,
394 icalproperty_new_uid(event->uid));
395 icalcomponent_add_property(ievent,
396 icalproperty_vanew_dtstamp(icaltime_from_timet(time(NULL), TRUE), 0));
397 icalcomponent_add_property(ievent,
398 icalproperty_vanew_dtstart((icaltime_from_string(event->dtstart)), 0));
399 icalcomponent_add_property(ievent,
400 icalproperty_vanew_dtend((icaltime_from_string(event->dtend)), 0));
401 if (event->recur && *(event->recur)) {
402 icalcomponent_add_property(ievent,
403 icalproperty_vanew_rrule((icalrecurrencetype_from_string(event->recur)), 0));
405 icalcomponent_add_property(ievent,
406 icalproperty_new_description(event->description));
407 icalcomponent_add_property(ievent,
408 icalproperty_new_summary(event->summary));
409 icalcomponent_add_property(ievent,
410 icalproperty_new_sequence(modif && !is_reply ? event->sequence + 1 : event->sequence));
411 icalcomponent_add_property(ievent,
412 icalproperty_new_class("PUBLIC"));
413 icalcomponent_add_property(ievent,
414 icalproperty_new_transp("OPAQUE"));
415 if (event->location && *event->location)
416 icalcomponent_add_property(ievent,
417 icalproperty_new_location(event->location));
419 icalcomponent_add_property(ievent,
420 icalproperty_new_location(""));
421 icalcomponent_add_property(ievent,
422 icalproperty_new_status(ICAL_STATUS_CONFIRMED));
423 icalcomponent_add_property(ievent,
424 icalproperty_vanew_created(icaltime_from_timet(time(NULL), TRUE), 0));
425 icalcomponent_add_property(ievent,
426 icalproperty_vanew_lastmodified(icaltime_from_timet(time(NULL), TRUE), 0));
427 icalcomponent_add_property(ievent,
430 if (!icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT))
431 icalcomponent_add_component(calendar, timezone);
433 icalcomponent_free(timezone);
435 icalcomponent_add_component(calendar, ievent);
437 if (event->url && *(event->url)) {
438 attprop = icalproperty_new_url(event->url);
439 icalcomponent_add_property(ievent, attprop);
443 /* dump only this attendee */
445 icalproperty_vanew_attendee(
447 icalparameter_new_role(
448 ICAL_ROLE_REQPARTICIPANT),
449 icalparameter_new_rsvp(ICAL_RSVP_TRUE),
450 icalparameter_new_partstat(status),
453 icalcomponent_add_property(ievent, attprop);
455 /* dump all attendees */
456 GSList *cur = event->answers;
457 while (cur && cur->data) {
458 Answer *a = (Answer *)cur->data;
461 a->cutype = ICAL_CUTYPE_INDIVIDUAL;
463 a->answer = ICAL_PARTSTAT_NEEDSACTION;
466 icalproperty_vanew_attendee(
468 icalparameter_new_role(
469 ICAL_ROLE_REQPARTICIPANT),
470 icalparameter_new_rsvp(ICAL_RSVP_TRUE),
471 icalparameter_new_cutype(a->cutype),
472 icalparameter_new_partstat(a->answer),
476 icalcomponent_add_property(ievent, attprop);
488 headers = write_headers(account, event, is_pseudo_event, is_reply, is_pseudo_event);
491 g_warning("can't get headers");
498 body = g_strdup_printf("%s"
500 "%s", headers, icalcomponent_as_ical_string(calendar));
502 if (str_write_to_file(body, tmpfile) < 0) {
507 chmod(tmpfile, S_IRUSR|S_IWUSR);
511 icalcomponent_free(calendar);
520 static void get_rfc822_date_from_time_t(gchar *buf, gint len, time_t t)
524 gchar day[4], mon[4];
525 gint dd, hh, mm, ss, yyyy;
529 lt = localtime_r(&t, &buft2);
530 sscanf(asctime_r(lt, buft1), "%3s %3s %d %d:%d:%d %d\n",
531 day, mon, &dd, &hh, &mm, &ss, &yyyy);
532 g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
533 day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
535 GDateTime *dt = g_date_time_new_from_unix_local(t);
536 gchar *buf2 = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
537 g_date_time_unref(dt);
538 strncpy(buf, buf2, len);
543 static gchar *write_headers_date(const gchar *uid)
551 memset(subject, 0, sizeof(subject));
552 memset(date, 0, sizeof(date));
554 if (!strcmp(uid, EVENT_PAST_ID)) {
556 t_subject = _("Past");
557 } else if (!strcmp(uid, EVENT_TODAY_ID)) {
559 t_subject = _("Today");
560 } else if (!strcmp(uid, EVENT_TOMORROW_ID)) {
561 t = time(NULL) + 86400;
562 t_subject = _("Tomorrow");
563 } else if (!strcmp(uid, EVENT_THISWEEK_ID)) {
564 t = time(NULL) + (86400*2);
565 t_subject = _("This week");
566 } else if (!strcmp(uid, EVENT_LATER_ID)) {
567 t = time(NULL) + (86400*7);
568 t_subject = _("Later");
570 g_warning("unknown spec date");
576 lt = *localtime_r(&t, &buft);
582 lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
584 get_rfc822_date_from_time_t(date, sizeof(date), t);
585 conv_encode_header(subject, 511, t_subject, strlen("Subject: "), FALSE);
587 return g_strdup_printf("From: -\n"
591 "MIME-Version: 1.0\n"
592 "Content-Type: text/plain; charset=\"UTF-8\";\n"
593 "Content-Transfer-Encoding: quoted-printable\n"
594 "Message-ID: <%s>\n",
600 gchar *vcal_manager_dateevent_dump(const gchar *uid, FolderItem *item)
602 gchar *sanitized_uid = NULL;
603 gchar *headers = NULL;
604 gchar *lines, *body, *tmpfile;
606 sanitized_uid = g_strdup(uid);
607 subst_for_filename(sanitized_uid);
609 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
610 G_DIR_SEPARATOR, getuid(), sanitized_uid);
611 g_free(sanitized_uid);
613 headers = write_headers_date(uid);
616 g_warning("can't get headers");
621 if (!strcmp(uid, EVENT_PAST_ID))
623 else if (!strcmp(uid, EVENT_TODAY_ID))
625 else if (!strcmp(uid, EVENT_TOMORROW_ID))
626 date = EVENT_TOMORROW;
627 else if (!strcmp(uid, EVENT_THISWEEK_ID))
628 date = EVENT_THISWEEK;
629 else if (!strcmp(uid, EVENT_LATER_ID))
634 lines = get_item_event_list_for_date(item, date);
635 body = g_strdup_printf("%s"
637 "%s", headers, lines);
639 if (str_write_to_file(body, tmpfile) < 0) {
643 chmod(tmpfile, S_IRUSR|S_IWUSR);
651 static gchar *write_headers_ical(PrefsAccount *account,
652 icalcomponent *ievent,
655 gchar *vcal_manager_icalevent_dump(icalcomponent *event, gchar *orga, icalcomponent *use_calendar)
657 PrefsAccount *account = account_get_cur_account();
658 gchar *body, *headers, *qpbody;
659 gchar **lines = NULL;
660 gchar *tmpfile = NULL;
661 icalcomponent *calendar;
663 icalcomponent *ievent = NULL;
666 ievent = icalcomponent_new_clone(event);
668 prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
670 gchar *sanitized_uid = g_strdup(icalproperty_get_uid(prop));
672 subst_for_filename(sanitized_uid);
674 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
675 G_DIR_SEPARATOR, getuid(), sanitized_uid);
676 g_free(sanitized_uid);
677 icalproperty_free(prop);
679 tmpfile = g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(),
680 G_DIR_SEPARATOR, getuid(), ievent);
685 icalcomponent_free(ievent);
691 if (use_calendar != NULL) {
692 calendar = use_calendar;
698 ICAL_VCALENDAR_COMPONENT,
699 icalproperty_new_version("2.0"),
700 icalproperty_new_prodid(
701 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
702 icalproperty_new_calscale("GREGORIAN"),
703 icalproperty_new_method(ICAL_METHOD_PUBLISH),
708 g_warning ("can't generate calendar");
710 icalcomponent_free(ievent);
714 icalcomponent_add_component(calendar, ievent);
719 headers = write_headers_ical(account, ievent, orga);
722 g_warning("can't get headers");
724 icalcomponent_free(calendar);
728 lines = g_strsplit(icalcomponent_as_ical_string(calendar), "\n", 0);
729 qpbody = g_strdup("");
731 /* encode to quoted-printable */
733 gint e_len = strlen(qpbody), n_len = 0;
734 gchar *outline = conv_codeset_strdup(lines[i], CS_UTF_8, conv_get_outgoing_charset_str());
735 gchar *qpoutline = g_malloc(strlen(outline)*8 + 1);
737 qp_encode_line(qpoutline, (guchar *)outline);
738 n_len = strlen(qpoutline);
740 qpbody = g_realloc(qpbody, e_len + n_len + 1);
741 strcpy(qpbody+e_len, qpoutline);
742 *(qpbody+n_len+e_len) = '\0';
749 body = g_strdup_printf("%s"
751 "%s", headers, qpbody);
753 if (str_write_to_file(body, tmpfile) < 0) {
757 chmod(tmpfile, S_IRUSR|S_IWUSR);
763 icalcomponent_free(calendar);
768 VCalEvent * vcal_manager_new_event (const gchar *uid,
769 const gchar *organizer,
770 const gchar *orgname,
771 const gchar *location,
772 const gchar *summary,
773 const gchar *description,
774 const gchar *dtstart,
779 enum icalproperty_method method,
781 enum icalcomponent_kind type)
783 VCalEvent *event = g_new0(VCalEvent, 1);
785 event->uid = g_strdup(uid?uid:"");
786 event->organizer = g_strdup(organizer?organizer:"");
787 event->orgname = g_strdup(orgname?orgname:"");
789 if (dtend && *(dtend)) {
790 time_t tmp = icaltime_as_timet((icaltime_from_string(dtend)));
792 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
793 event->end = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
794 g_date_time_unref(dt);
798 event->end = g_strdup(ctime_r(&tmp, buft));
802 if (dtstart && *(dtstart)) {
803 time_t tmp = icaltime_as_timet((icaltime_from_string(dtstart)));
805 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
806 event->start = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
807 g_date_time_unref(dt);
811 event->start = g_strdup(ctime_r(&tmp, buft));
814 event->dtstart = g_strdup(dtstart?dtstart:"");
815 event->dtend = g_strdup(dtend?dtend:"");
816 event->recur = g_strdup(recur?recur:"");
817 event->location = g_strdup(location?location:"");
818 event->summary = g_strdup(summary?summary:"");
819 event->description = g_strdup(description?description:"");
820 event->url = g_strdup(url?url:"");
821 event->tzid = g_strdup(tzid?tzid:"");
822 event->method = method;
823 event->sequence = sequence;
825 event->rec_occurence = FALSE;
826 while (strchr(event->summary, '\n'))
827 *(strchr(event->summary, '\n')) = ' ';
832 void vcal_manager_free_event (VCalEvent *event)
839 g_free(event->organizer);
840 g_free(event->orgname);
841 g_free(event->start);
843 g_free(event->location);
844 g_free(event->summary);
845 g_free(event->dtstart);
846 g_free(event->dtend);
847 g_free(event->recur);
849 g_free(event->description);
851 for (cur = event->answers; cur; cur = cur->next) {
852 answer_free((Answer *)cur->data);
854 g_slist_free(event->answers);
858 gchar *vcal_manager_get_event_path(void)
860 static gchar *event_path = NULL;
862 event_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
868 gchar *vcal_manager_get_event_file(const gchar *uid)
870 gchar *tmp = g_strdup(uid);
873 subst_for_filename(tmp);
874 res = g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S,
880 PrefsAccount *vcal_manager_get_account_from_event(VCalEvent *event)
882 GSList *list = vcal_manager_get_answers_emails(event);
885 /* find an attendee corresponding to one of our accounts */
886 while (cur && cur->data) {
887 gchar *email = (gchar *)cur->data;
888 if (account_find_from_address(email, FALSE)) {
890 return account_find_from_address(email, FALSE);
898 void vcal_manager_save_event (VCalEvent *event, gboolean export_after)
901 XMLNode *xmlnode = NULL;
902 GNode *rootnode = NULL;
905 GSList *answers = event->answers;
907 gint tmp_method = event->method;
909 tag = xml_tag_new("event");
910 xml_tag_add_attr(tag, xml_attr_new("organizer", event->organizer));
911 xml_tag_add_attr(tag, xml_attr_new("orgname", event->orgname));
912 xml_tag_add_attr(tag, xml_attr_new("location", event->location));
913 xml_tag_add_attr(tag, xml_attr_new("summary", event->summary));
914 xml_tag_add_attr(tag, xml_attr_new("description", event->description));
915 xml_tag_add_attr(tag, xml_attr_new("url", event->url));
916 xml_tag_add_attr(tag, xml_attr_new("dtstart", event->dtstart));
917 xml_tag_add_attr(tag, xml_attr_new("dtend", event->dtend));
918 xml_tag_add_attr(tag, xml_attr_new("recur", event->recur));
919 xml_tag_add_attr(tag, xml_attr_new("tzid", event->tzid));
921 /* updating answers saves events, don't save them with reply type */
922 if (tmp_method == ICAL_METHOD_REPLY)
923 tmp_method = ICAL_METHOD_REQUEST;
925 tmp = g_strdup_printf("%d", tmp_method);
926 xml_tag_add_attr(tag, xml_attr_new("method", tmp));
929 tmp = g_strdup_printf("%d", event->sequence);
930 xml_tag_add_attr(tag, xml_attr_new("sequence", tmp));
933 tmp = g_strdup_printf("%d", event->type);
934 xml_tag_add_attr(tag, xml_attr_new("type", tmp));
937 tmp = g_strdup_printf("%lld", (long long)event->postponed);
938 xml_tag_add_attr(tag, xml_attr_new("postponed", tmp));
941 tmp = g_strdup_printf("%d", event->rec_occurence);
942 xml_tag_add_attr(tag, xml_attr_new("rec_occurence", tmp));
945 xmlnode = xml_node_new(tag, NULL);
946 rootnode = g_node_new(xmlnode);
948 while (answers && answers->data) {
949 XMLNode *ansxmlnode = NULL;
950 GNode *ansnode = NULL;
951 XMLTag *anstag = xml_tag_new("answer");
952 Answer *a = (Answer *)answers->data;
953 xml_tag_add_attr(anstag, xml_attr_new("attendee", a->attendee));
954 xml_tag_add_attr(anstag, xml_attr_new("name", a->name?a->name:""));
955 tmp = g_strdup_printf("%d", a->answer);
956 xml_tag_add_attr(anstag, xml_attr_new("answer", tmp));
958 tmp = g_strdup_printf("%d", a->cutype);
959 xml_tag_add_attr(anstag, xml_attr_new("cutype", tmp));
961 ansxmlnode = xml_node_new(anstag, NULL);
962 ansnode = g_node_new(ansxmlnode);
963 g_node_append(rootnode, ansnode);
964 answers = answers->next;
967 path = vcal_manager_get_event_file(event->uid);
969 if ((pfile = prefs_write_open(path)) == NULL) {
970 gchar *dir_path = vcal_manager_get_event_path();
971 if (!is_dir_exist(dir_path) && make_dir(vcal_manager_get_event_path()) != 0) {
977 if ((pfile = prefs_write_open(path)) == NULL) {
984 xml_file_put_xml_decl(pfile->fp);
985 xml_write_tree(rootnode, pfile->fp);
986 xml_free_tree(rootnode);
988 if (prefs_file_close(pfile) < 0) {
989 g_warning("failed to write event.");
994 vcal_folder_export(NULL);
997 static VCalEvent *event_get_from_xml (const gchar *uid, GNode *node)
1001 gchar *org = NULL, *location = NULL, *summary = NULL, *orgname = NULL;
1002 gchar *dtstart = NULL, *dtend = NULL, *tzid = NULL;
1003 gchar *description = NULL, *url = NULL, *recur = NULL;
1004 VCalEvent *event = NULL;
1005 enum icalproperty_method method = ICAL_METHOD_REQUEST;
1006 enum icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
1007 gint sequence = 0, rec_occurence = 0;
1008 time_t postponed = (time_t)0;
1010 g_return_val_if_fail(node->data != NULL, NULL);
1012 xmlnode = node->data;
1013 if (strcmp2(xmlnode->tag->tag, "event") != 0) {
1014 g_warning("tag name != \"event\"");
1018 list = xmlnode->tag->attr;
1019 for (; list != NULL; list = list->next) {
1020 XMLAttr *attr = list->data;
1022 if (!attr || !attr->name || !attr->value) continue;
1023 if (!strcmp(attr->name, "organizer"))
1024 org = g_strdup(attr->value);
1025 if (!strcmp(attr->name, "orgname"))
1026 orgname = g_strdup(attr->value);
1027 if (!strcmp(attr->name, "location"))
1028 location = g_strdup(attr->value);
1029 if (!strcmp(attr->name, "summary"))
1030 summary = g_strdup(attr->value);
1031 if (!strcmp(attr->name, "description"))
1032 description = g_strdup(attr->value);
1033 if (!strcmp(attr->name, "url"))
1034 url = g_strdup(attr->value);
1035 if (!strcmp(attr->name, "dtstart"))
1036 dtstart = g_strdup(attr->value);
1037 if (!strcmp(attr->name, "dtend"))
1038 dtend = g_strdup(attr->value);
1039 if (!strcmp(attr->name, "recur"))
1040 recur = g_strdup(attr->value);
1041 if (!strcmp(attr->name, "tzid"))
1042 tzid = g_strdup(attr->value);
1043 if (!strcmp(attr->name, "type"))
1044 type = atoi(attr->value);
1045 if (!strcmp(attr->name, "method"))
1046 method = atoi(attr->value);
1047 if (!strcmp(attr->name, "sequence"))
1048 sequence = atoi(attr->value);
1049 if (!strcmp(attr->name, "postponed"))
1050 postponed = atoi(attr->value);
1051 if (!strcmp(attr->name, "rec_occurence"))
1052 rec_occurence = atoi(attr->value);
1055 event = vcal_manager_new_event(uid, org, orgname, location, summary, description,
1056 dtstart, dtend, recur, tzid, url, method,
1059 event->postponed = postponed;
1060 event->rec_occurence = rec_occurence;
1066 g_free(description);
1073 node = node->children;
1074 while (node != NULL) {
1075 gchar *attendee = NULL;
1077 enum icalparameter_partstat answer = ICAL_PARTSTAT_NEEDSACTION;
1078 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1080 xmlnode = node->data;
1081 if (strcmp2(xmlnode->tag->tag, "answer") != 0) {
1082 g_warning("tag name != \"answer\"");
1085 list = xmlnode->tag->attr;
1086 for (; list != NULL; list = list->next) {
1087 XMLAttr *attr = list->data;
1089 if (!attr || !attr->name || !attr->value) continue;
1090 if (!strcmp(attr->name, "attendee"))
1091 attendee = g_strdup(attr->value);
1092 if (!strcmp(attr->name, "name"))
1093 name = g_strdup(attr->value);
1094 if (!strcmp(attr->name, "answer"))
1095 answer = atoi(attr->value);
1096 if (!strcmp(attr->name, "cutype"))
1097 cutype = atoi(attr->value);
1100 event->answers = g_slist_prepend(event->answers, answer_new(attendee, name, answer, cutype));
1105 event->answers = g_slist_reverse(event->answers);
1110 VCalEvent *vcal_manager_load_event (const gchar *uid)
1114 VCalEvent *event = NULL;
1116 path = vcal_manager_get_event_file(uid);
1118 if (!is_file_exist(path)) {
1123 node = xml_parse_file(path);
1128 g_warning("no node");
1132 event = event_get_from_xml(uid, node);
1133 /* vcal_manager_event_print(event); */
1134 xml_free_tree(node);
1139 while (strchr(event->summary, '\n'))
1140 *(strchr(event->summary, '\n')) = ' ';
1146 void vcal_manager_update_answer (VCalEvent *event,
1147 const gchar *attendee,
1149 enum icalparameter_partstat ans,
1150 enum icalparameter_cutype cutype)
1152 Answer *answer = NULL;
1153 GSList *existing = NULL;
1154 Answer *existing_a = NULL;
1159 answer = answer_new(attendee, name, ans, cutype);
1160 existing = answer_find(event, answer);
1163 existing_a = (Answer *)existing->data;
1165 if (!answer->name && existing_a->name)
1166 answer->name = g_strdup(existing_a->name);
1167 if (!answer->cutype && existing_a->cutype)
1168 answer->cutype = existing_a->cutype;
1170 answer_remove(event, answer);
1173 answer_add(event, answer);
1175 vcal_manager_save_event(event, FALSE);
1178 static gchar *write_headers(PrefsAccount *account,
1180 gboolean short_headers,
1182 gboolean is_pseudo_display)
1184 gchar *subject = NULL;
1186 gchar *save_folder = NULL;
1187 gchar *result = NULL;
1188 gchar *queue_headers = NULL;
1189 gchar *method_str = NULL;
1190 gchar *attendees = NULL;
1191 enum icalparameter_partstat status;
1192 gchar *prefix = NULL;
1193 gchar enc_subject[512], enc_from[512], *from = NULL;
1195 gchar *calmsgid = NULL;
1197 cm_return_val_if_fail(account != NULL, NULL);
1199 memset(date, 0, sizeof(date));
1201 if (is_pseudo_display) {
1202 struct icaltimetype itt = (icaltime_from_string(event->dtstart));
1203 time_t t = icaltime_as_timet(itt);
1204 get_rfc822_date_from_time_t(date, sizeof(date), t);
1206 get_rfc822_date(date, sizeof(date));
1209 if (account_get_special_folder(account, F_OUTBOX)) {
1210 save_folder = folder_item_get_identifier(account_get_special_folder
1211 (account, F_OUTBOX));
1215 GSList *cur = event->answers;
1216 while (cur && cur->data) {
1218 Answer *a = (Answer *)cur->data;
1220 if (strcasecmp(a->attendee, event->organizer)) {
1222 tmp = g_strdup_printf("%s>,\n <%s", attendees, a->attendee);
1226 attendees = g_strdup_printf("%s", a->attendee);
1233 if (!short_headers) {
1234 queue_headers = g_strdup_printf("S:%s\n"
1239 "X-Claws-End-Special-Headers: 1\n",
1241 account->smtp_server,
1242 is_reply ? event->organizer:attendees,
1243 account->account_id,
1244 save_folder?"SCF:":"",
1245 save_folder?save_folder:"",
1246 save_folder?"\n":"");
1248 queue_headers = g_strdup("");
1253 method_str = "REPLY";
1254 status = vcal_manager_get_reply_for_attendee(event, account->address);
1255 if (status == ICAL_PARTSTAT_ACCEPTED)
1256 prefix = _("Accepted: ");
1257 else if (status == ICAL_PARTSTAT_DECLINED)
1258 prefix = _("Declined: ");
1259 else if (status == ICAL_PARTSTAT_TENTATIVE)
1260 prefix = _("Tentatively Accepted: ");
1263 } else if (event->method == ICAL_METHOD_PUBLISH) {
1264 method_str = "PUBLISH";
1265 } else if (event->method == ICAL_METHOD_CANCEL) {
1266 method_str = "CANCEL";
1268 method_str = "REQUEST";
1271 subject = g_strdup_printf("%s%s", prefix, event->summary);
1273 conv_encode_header_full(enc_subject, sizeof(enc_subject), subject, strlen("Subject: "),
1274 FALSE, conv_get_outgoing_charset_str());
1275 from = is_reply?account->name:(event->orgname?event->orgname:"");
1276 conv_encode_header_full(enc_from, sizeof(enc_from), from, strlen("From: "),
1277 TRUE, conv_get_outgoing_charset_str());
1279 if (is_pseudo_display && event->uid) {
1280 calmsgid = g_strdup_printf("Message-ID: <%s>\n",event->uid);
1282 calmsgid = g_strdup("");
1285 if (account && account->set_domain && account->domain) {
1286 g_snprintf(msgid, sizeof(msgid), "%s", account->domain);
1287 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
1288 g_snprintf(msgid, sizeof(msgid), "%s",
1289 strchr(account->address, '@') ?
1290 strchr(account->address, '@')+1 :
1293 g_snprintf(msgid, sizeof(msgid), "%s", "");
1296 generate_msgid(msgid, sizeof(msgid), account->address);
1298 result = g_strdup_printf("%s"
1303 "MIME-Version: 1.0\n"
1304 "Content-Type: text/calendar; method=%s; charset=\"%s\"\n"
1305 "Content-Transfer-Encoding: 8bit\n"
1310 is_reply ? account->address:event->organizer,
1311 is_reply ? event->organizer:(attendees?attendees:event->organizer),
1318 "In-Reply-To":"Message-ID",
1320 event_to_today_str(event, 0):msgid);
1323 g_free(save_folder);
1324 g_free(queue_headers);
1331 static gchar *write_headers_ical(PrefsAccount *account,
1332 icalcomponent *ievent,
1337 gchar *result = NULL;
1338 gchar *method_str = NULL;
1339 gchar *summary = NULL;
1340 gchar *organizer = NULL;
1341 gchar *orgname = NULL;
1342 icalproperty *prop = NULL;
1343 gchar *calmsgid = NULL;
1345 time_t t = (time_t)0;
1347 memset(subject, 0, sizeof(subject));
1348 memset(date, 0, sizeof(date));
1350 prop = icalcomponent_get_first_property(ievent, ICAL_SUMMARY_PROPERTY);
1352 summary = g_strdup(icalproperty_get_summary(prop));
1353 icalproperty_free(prop);
1355 summary = g_strdup("");
1358 while (strchr(summary, '\n'))
1359 *(strchr(summary, '\n')) = ' ';
1361 prop = icalcomponent_get_first_property(ievent, ICAL_ORGANIZER_PROPERTY);
1363 organizer = g_strdup(icalproperty_get_organizer(prop));
1364 if (icalproperty_get_parameter_as_string(prop, "CN") != NULL)
1365 orgname = g_strdup(icalproperty_get_parameter_as_string(prop, "CN"));
1367 icalproperty_free(prop);
1369 organizer = orga? g_strdup(orga):g_strdup("");
1372 prop = icalcomponent_get_first_property(ievent, ICAL_DTSTART_PROPERTY);
1374 t = icaltime_as_timet(icalproperty_get_dtstart(prop));
1375 get_rfc822_date_from_time_t(date, sizeof(date), t);
1377 get_rfc822_date(date, sizeof(date));
1380 conv_encode_header(subject, 511, summary, strlen("Subject: "), FALSE);
1382 method_str = "PUBLISH";
1384 prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
1386 calmsgid = g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop));
1387 icalproperty_free(prop);
1389 calmsgid = g_strdup("");
1393 result = g_strdup_printf("From: %s <%s>\n"
1397 "MIME-Version: 1.0\n"
1398 "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n"
1399 "Content-Transfer-Encoding: quoted-printable\n"
1401 "In-Reply-To: <%s>\n",
1403 !strncmp(organizer, "MAILTO:", 7) ? organizer+7 : organizer,
1409 conv_get_outgoing_charset_str(),
1411 event_to_today_str(NULL, t));
1423 static gboolean vcal_manager_send (PrefsAccount *account,
1427 gchar *tmpfile = NULL;
1429 FolderItem *folderitem;
1430 gchar *msgpath = NULL;
1431 Folder *folder = NULL;
1433 tmpfile = vcal_manager_event_dump(event, is_reply, FALSE, NULL, TRUE);
1438 folderitem = account_get_special_folder(account, F_QUEUE);
1440 g_warning("can't find queue folder for %s", account->address);
1445 folder_item_scan(folderitem);
1447 if ((msgnum = folder_item_add_msg(folderitem, tmpfile, NULL, TRUE)) < 0) {
1448 g_warning("can't queue the message");
1454 msgpath = folder_item_fetch_msg(folderitem, msgnum);
1456 if (!prefs_common_get_prefs()->work_offline) {
1458 gboolean queued_removed = FALSE;
1459 gint val = procmsg_send_message_queue_with_lock(msgpath, &err, folderitem, msgnum, &queued_removed);
1461 if (!queued_removed)
1462 folder_item_remove_msg(folderitem, msgnum);
1463 folder_item_scan(folderitem);
1465 alertpanel_error_log("%s", err);
1473 folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1475 folder_item_scan(folder->inbox);
1477 vcalviewer_reload(folder->inbox);
1481 gboolean vcal_manager_reply (PrefsAccount *account,
1484 return vcal_manager_send(account, event, TRUE);
1487 gboolean vcal_manager_request (PrefsAccount *account,
1490 return vcal_manager_send(account, event, FALSE);
1493 EventTime event_to_today(VCalEvent *event, time_t t)
1495 struct tm evtstart, today;
1496 time_t evtstart_t, today_t;
1497 struct icaltimetype itt;
1501 today_t = time(NULL);
1503 itt = icaltime_from_string(event->dtstart);
1504 evtstart_t = icaltime_as_timet(itt);
1511 today = *localtime_r(&today_t, &buft);
1512 localtime_r(&evtstart_t, &evtstart);
1518 today = *localtime(&today_t);
1519 evtstart = *localtime(&evtstart_t);
1522 if (today.tm_year == evtstart.tm_year) {
1523 int days = evtstart.tm_yday - today.tm_yday;
1526 } else if (days == 0) {
1528 } else if (days == 1) {
1529 return EVENT_TOMORROW;
1530 } else if (days > 1 && days < 7) {
1531 return EVENT_THISWEEK;
1535 } else if (today.tm_year > evtstart.tm_year) {
1537 } else if (today.tm_year == evtstart.tm_year - 1) {
1538 int days = ((365 - today.tm_yday) + evtstart.tm_yday);
1541 } else if (days == 1) {
1542 return EVENT_TOMORROW;
1543 } else if (days > 1 && days < 7) {
1544 return EVENT_THISWEEK;
1552 const gchar *event_to_today_str(VCalEvent *event, time_t t)
1554 EventTime days = event_to_today(event, t);
1557 return EVENT_PAST_ID;
1559 return EVENT_TODAY_ID;
1560 case EVENT_TOMORROW:
1561 return EVENT_TOMORROW_ID;
1562 case EVENT_THISWEEK:
1563 return EVENT_THISWEEK_ID;
1565 return EVENT_LATER_ID;