2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2023 the Claws Mail team and Colin Leroy <colin@colino.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "claws-features.h"
27 #include <glib/gi18n.h>
29 #include <sys/types.h>
34 #include <libical/ical.h>
35 #include "vcalendar.h"
36 #include "vcal_folder.h"
37 #include "vcal_manager.h"
38 #include "vcal_meeting_gtk.h"
39 #include "vcal_prefs.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "alertpanel.h"
50 #include "quoted-printable.h"
51 #include "file-utils.h"
59 Answer
*answer_new(const gchar
*attendee
,
61 icalparameter_partstat ans
,
62 icalparameter_cutype cutype
)
64 Answer
*answer
= g_new0(Answer
, 1);
65 answer
->attendee
= g_strdup(attendee
);
66 answer
->name
= g_strdup(name
);
68 answer
->name
= g_strdup("");
69 if (!answer
->attendee
)
70 answer
->attendee
= g_strdup("");
72 answer
->cutype
= cutype
;
76 static void answer_free(Answer
*answer
)
78 g_free(answer
->attendee
);
83 static GSList
*answer_find(VCalEvent
*event
, Answer
*answer
)
85 GSList
*cur
= event
->answers
;
86 while (cur
&& cur
->data
) {
87 Answer
*b
= (Answer
*)cur
->data
;
88 if (!strcasecmp(b
->attendee
, answer
->attendee
))
95 void vcal_manager_copy_attendees(VCalEvent
*src
, VCalEvent
*dest
)
97 GSList
*cur
= src
->answers
;
98 while (cur
&& cur
->data
) {
99 Answer
*a
= (Answer
*)cur
->data
;
100 Answer
*b
= answer_new(a
->attendee
, a
->name
, a
->answer
, a
->cutype
);
101 dest
->answers
= g_slist_prepend(dest
->answers
, b
);
104 dest
->answers
= g_slist_reverse(dest
->answers
);
107 gchar
*vcal_manager_answer_get_text(icalparameter_partstat ans
)
109 static gchar
*replies
[5]={
111 N_("tentatively accepted"),
113 N_("did not answer"),
118 case ICAL_PARTSTAT_ACCEPTED
:
119 return _(replies
[0]);
121 case ICAL_PARTSTAT_DECLINED
:
122 return _(replies
[2]);
124 case ICAL_PARTSTAT_TENTATIVE
:
125 return _(replies
[1]);
127 case ICAL_PARTSTAT_NEEDSACTION
:
128 return _(replies
[3]);
129 case ICAL_PARTSTAT_DELEGATED
:
130 case ICAL_PARTSTAT_COMPLETED
:
131 case ICAL_PARTSTAT_X
:
132 case ICAL_PARTSTAT_INPROCESS
:
133 case ICAL_PARTSTAT_NONE
:
134 case ICAL_PARTSTAT_FAILED
:
135 return _(replies
[4]);
141 gchar
*vcal_manager_cutype_get_text(icalparameter_cutype type
)
143 static gchar
*replies
[5]={
152 case ICAL_CUTYPE_INDIVIDUAL
:
153 return _(replies
[0]);
155 case ICAL_CUTYPE_GROUP
:
156 return _(replies
[1]);
158 case ICAL_CUTYPE_RESOURCE
:
159 return _(replies
[2]);
161 case ICAL_CUTYPE_ROOM
:
162 return _(replies
[3]);
164 return _(replies
[4]);
170 static GSList
*answer_remove(VCalEvent
*event
, Answer
*answer
)
172 GSList
*cur
= answer_find(event
, answer
);
174 Answer
*b
= (Answer
*)cur
->data
;
175 event
->answers
= g_slist_remove(event
->answers
, b
);
178 return event
->answers
;
181 static GSList
*answer_add(VCalEvent
*event
, Answer
*answer
)
183 event
->answers
= g_slist_append(event
->answers
, answer
);
184 return event
->answers
;
187 GSList
*vcal_manager_get_answers_emails(VCalEvent
*event
)
190 GSList
*cur
= event
->answers
;
191 while (cur
&& cur
->data
) {
192 Answer
*b
= (Answer
*)cur
->data
;
193 new = g_slist_prepend(new, b
->attendee
);
196 new = g_slist_reverse(new);
200 icalparameter_partstat
vcal_manager_get_reply_for_attendee(VCalEvent
*event
, const gchar
*att
)
202 Answer
*a
= answer_new(att
, NULL
, 0, 0);
203 GSList
*ans
= answer_find(event
, a
);
204 icalparameter_partstat res
= 0;
206 Answer
*b
= (Answer
*)ans
->data
;
213 gchar
*vcal_manager_get_cutype_text_for_attendee(VCalEvent
*event
, const gchar
*att
)
215 icalparameter_cutype status
= vcal_manager_get_cutype_for_attendee(event
, att
);
218 res
= g_strdup(vcal_manager_cutype_get_text(status
));
223 icalparameter_cutype
vcal_manager_get_cutype_for_attendee(VCalEvent
*event
, const gchar
*att
)
225 Answer
*a
= answer_new(att
, NULL
, 0, 0);
226 GSList
*ans
= answer_find(event
, a
);
227 icalparameter_cutype res
= 0;
229 Answer
*b
= (Answer
*)ans
->data
;
236 gchar
*vcal_manager_get_reply_text_for_attendee(VCalEvent
*event
, const gchar
*att
)
238 icalparameter_partstat status
= vcal_manager_get_reply_for_attendee(event
, att
);
241 res
= g_strdup(vcal_manager_answer_get_text(status
));
246 gchar
*vcal_manager_get_attendee_name(VCalEvent
*event
, const gchar
*att
)
248 Answer
*a
= answer_new(att
, NULL
, 0, 0);
249 GSList
*ans
= answer_find(event
, a
);
252 Answer
*b
= (Answer
*)ans
->data
;
254 res
= g_strdup(b
->name
);
260 void vcal_manager_event_print(VCalEvent
*event
)
262 GSList
*list
= event
->answers
;
263 printf( "event->uid\t\t%s\n"
264 "event->organizer\t\t%s\n"
265 "event->start\t\t%s\n"
267 "event->location\t\t%s\n"
268 "event->summary\t\t%s\n"
269 "event->description\t%s\n"
271 "event->dtstart\t\t%s\n"
272 "event->dtend\t\t%s\n"
273 "event->recur\t\t%s\n"
274 "event->tzid\t\t%s\n"
275 "event->method\t\t%d\n"
276 "event->sequence\t\t%d\n",
291 while (list
&& list
->data
) {
292 Answer
*a
= (Answer
*)list
->data
;
293 printf(" ans: %s %s, %s\n", a
->name
, a
->attendee
, vcal_manager_answer_get_text(a
->answer
));
299 static gchar
*write_headers(PrefsAccount
*account
,
301 gboolean short_headers
,
303 gboolean is_pseudo_event
);
305 gchar
*vcal_manager_event_dump(VCalEvent
*event
, gboolean is_reply
, gboolean is_pseudo_event
,
306 icalcomponent
*use_calendar
, gboolean modif
)
308 gchar
*organizer
= g_strdup_printf("MAILTO:%s", event
->organizer
);
309 PrefsAccount
*account
= vcal_manager_get_account_from_event(event
);
310 gchar
*attendee
= NULL
;
311 gchar
*body
, *headers
;
312 gchar
*tmpfile
= NULL
;
313 icalcomponent
*calendar
, *ievent
, *timezone
, *tzc
;
314 icalproperty
*attprop
;
315 icalproperty
*orgprop
;
316 icalparameter_partstat status
= ICAL_PARTSTAT_NEEDSACTION
;
317 gchar
*sanitized_uid
= g_strdup(event
->uid
);
319 subst_for_filename(sanitized_uid
);
321 tmpfile
= g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
322 G_DIR_SEPARATOR
, getuid(), sanitized_uid
);
323 g_free(sanitized_uid
);
328 debug_print("no account found\n");
332 attendee
= g_strdup_printf("MAILTO:%s", account
->address
);
334 if (vcal_manager_get_reply_for_attendee(event
, account
->address
) != 0)
335 status
= vcal_manager_get_reply_for_attendee(event
, account
->address
);
339 if (use_calendar
!= NULL
) {
340 calendar
= use_calendar
;
346 ICAL_VCALENDAR_COMPONENT
,
347 icalproperty_new_version("2.0"),
348 icalproperty_new_prodid(
349 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
350 icalproperty_new_calscale("GREGORIAN"),
351 icalproperty_new_method(is_reply
? ICAL_METHOD_REPLY
:event
->method
),
356 g_warning("can't generate calendar");
363 orgprop
= icalproperty_new_organizer(organizer
);
365 timezone
= icalcomponent_new(ICAL_VTIMEZONE_COMPONENT
);
367 icalcomponent_add_property(timezone
,
368 icalproperty_new_tzid("UTC")); /* free */
370 tzc
= icalcomponent_new(ICAL_XSTANDARD_COMPONENT
);
371 icalcomponent_add_property(tzc
,
372 icalproperty_new_dtstart(
373 icaltime_from_string("19700101T000000")));
374 icalcomponent_add_property(tzc
,
375 icalproperty_new_tzoffsetfrom(0.0));
376 icalcomponent_add_property(tzc
,
377 icalproperty_new_tzoffsetto(0.0));
378 icalcomponent_add_property(tzc
,
379 icalproperty_new_tzname("Greenwich meridian time"));
381 icalcomponent_add_component(timezone
, tzc
);
385 ICAL_VEVENT_COMPONENT
, (void*)0);
388 g_warning("can't generate event");
395 icalcomponent_add_property(ievent
,
396 icalproperty_new_uid(event
->uid
));
397 icalcomponent_add_property(ievent
,
398 icalproperty_vanew_dtstamp(icaltime_from_timet_with_zone(time(NULL
), TRUE
, NULL
), (void*)0));
399 icalcomponent_add_property(ievent
,
400 icalproperty_vanew_dtstart((icaltime_from_string(event
->dtstart
)), (void*)0));
401 icalcomponent_add_property(ievent
,
402 icalproperty_vanew_dtend((icaltime_from_string(event
->dtend
)), (void*)0));
403 if (event
->recur
&& *(event
->recur
)) {
404 icalcomponent_add_property(ievent
,
405 icalproperty_vanew_rrule((icalrecurrencetype_from_string(event
->recur
)), (void*)0));
407 icalcomponent_add_property(ievent
,
408 icalproperty_new_description(event
->description
));
409 icalcomponent_add_property(ievent
,
410 icalproperty_new_summary(event
->summary
));
411 icalcomponent_add_property(ievent
,
412 icalproperty_new_sequence(modif
&& !is_reply
? event
->sequence
+ 1 : event
->sequence
));
413 icalcomponent_add_property(ievent
,
414 icalproperty_new_class(ICAL_CLASS_PUBLIC
));
415 icalcomponent_add_property(ievent
,
416 icalproperty_new_transp(ICAL_TRANSP_OPAQUE
));
418 if (event
->location
&& *event
->location
)
419 icalcomponent_add_property(ievent
,
420 icalproperty_new_location(event
->location
));
422 icalcomponent_add_property(ievent
,
423 icalproperty_new_location(""));
424 icalcomponent_add_property(ievent
,
425 icalproperty_new_status(ICAL_STATUS_CONFIRMED
));
426 if (event
->created
!= NULL
&& event
->created
[0] != '\0') {
427 icalcomponent_add_property(ievent
,
428 icalproperty_vanew_created(icaltime_from_string(event
->created
), (void*)0));
430 icalcomponent_add_property(ievent
,
431 icalproperty_vanew_created(icaltime_from_timet_with_zone(time(NULL
), FALSE
, NULL
), (void*)0));
433 if (event
->last_modified
!= NULL
&& event
->last_modified
[0] != '\0' && !modif
) {
434 icalcomponent_add_property(ievent
,
435 icalproperty_vanew_lastmodified(icaltime_from_string(event
->last_modified
), (void*)0));
437 icalcomponent_add_property(ievent
,
438 icalproperty_vanew_lastmodified(icaltime_from_timet_with_zone(time(NULL
), FALSE
, NULL
), (void*)0));
440 icalcomponent_add_property(ievent
,
443 if (!icalcomponent_get_first_component(calendar
, ICAL_VTIMEZONE_COMPONENT
))
444 icalcomponent_add_component(calendar
, timezone
);
446 icalcomponent_free(timezone
);
448 icalcomponent_add_component(calendar
, ievent
);
450 if (event
->url
&& *(event
->url
)) {
451 attprop
= icalproperty_new_url(event
->url
);
452 icalcomponent_add_property(ievent
, attprop
);
456 /* dump only this attendee */
458 icalproperty_vanew_attendee(
460 icalparameter_new_role(
461 ICAL_ROLE_REQPARTICIPANT
),
462 icalparameter_new_rsvp(ICAL_RSVP_TRUE
),
463 icalparameter_new_partstat(status
),
466 icalcomponent_add_property(ievent
, attprop
);
468 /* dump all attendees */
469 GSList
*cur
= event
->answers
;
470 while (cur
&& cur
->data
) {
471 Answer
*a
= (Answer
*)cur
->data
;
474 a
->cutype
= ICAL_CUTYPE_INDIVIDUAL
;
476 a
->answer
= ICAL_PARTSTAT_NEEDSACTION
;
479 icalproperty_vanew_attendee(
481 icalparameter_new_role(
482 ICAL_ROLE_REQPARTICIPANT
),
483 icalparameter_new_rsvp(ICAL_RSVP_TRUE
),
484 icalparameter_new_cutype(a
->cutype
),
485 icalparameter_new_partstat(a
->answer
),
489 icalcomponent_add_property(ievent
, attprop
);
501 headers
= write_headers(account
, event
, is_pseudo_event
, is_reply
, is_pseudo_event
);
504 g_warning("can't get headers");
511 body
= g_strdup_printf("%s"
513 "%s", headers
, icalcomponent_as_ical_string(calendar
));
515 if (str_write_to_file(body
, tmpfile
, FALSE
) < 0) {
520 chmod(tmpfile
, S_IRUSR
|S_IWUSR
);
524 icalcomponent_free(calendar
);
533 static void get_rfc822_date_from_time_t(gchar
*buf
, gint len
, time_t t
)
537 gchar day
[4], mon
[4];
538 gint dd
, hh
, mm
, ss
, yyyy
;
542 lt
= localtime_r(&t
, &buft2
);
543 if (sscanf(asctime_r(lt
, buft1
), "%3s %3s %d %d:%d:%d %d\n",
544 day
, mon
, &dd
, &hh
, &mm
, &ss
, &yyyy
) != 7)
545 g_warning("failed reading date/time");
546 g_snprintf(buf
, len
, "%s, %d %s %d %02d:%02d:%02d %s",
547 day
, dd
, mon
, yyyy
, hh
, mm
, ss
, tzoffset(&t
));
549 GDateTime
*dt
= g_date_time_new_from_unix_local(t
);
551 g_warning("failed getting date/time");
552 g_snprintf(buf
, len
, "(NULL)");
556 gchar
*ret
= g_date_time_format(dt
, "%a, %e %b %Y %T %z");
557 g_date_time_unref(dt
);
560 g_warning("failed formatting date/time");
561 g_snprintf(buf
, len
, "(NULL)");
565 g_snprintf(buf
, len
, ret
);
570 static gchar
*write_headers_date(const gchar
*uid
)
574 gchar date
[RFC822_DATE_BUFFSIZE
];
578 memset(subject
, 0, sizeof(subject
));
579 memset(date
, 0, sizeof(date
));
581 if (!strcmp(uid
, EVENT_PAST_ID
)) {
583 t_subject
= _("Past");
584 } else if (!strcmp(uid
, EVENT_TODAY_ID
)) {
586 t_subject
= _("Today");
587 } else if (!strcmp(uid
, EVENT_TOMORROW_ID
)) {
588 t
= time(NULL
) + 86400;
589 t_subject
= _("Tomorrow");
590 } else if (!strcmp(uid
, EVENT_THISWEEK_ID
)) {
591 t
= time(NULL
) + (86400*2);
592 t_subject
= _("This week");
593 } else if (!strcmp(uid
, EVENT_LATER_ID
)) {
594 t
= time(NULL
) + (86400*7);
595 t_subject
= _("Later");
597 g_warning("unknown spec date");
603 lt
= *localtime_r(&t
, &buft
);
609 lt
.tm_hour
= lt
.tm_min
= lt
.tm_sec
= 0;
611 get_rfc822_date_from_time_t(date
, sizeof(date
), t
);
612 conv_encode_header(subject
, 511, t_subject
, strlen("Subject: "), FALSE
);
614 return g_strdup_printf("From: -\n"
618 "MIME-Version: 1.0\n"
619 "Content-Type: text/plain; charset=\"UTF-8\";\n"
620 "Content-Transfer-Encoding: quoted-printable\n"
621 "Message-ID: <%s>\n",
627 gchar
*vcal_manager_dateevent_dump(const gchar
*uid
, FolderItem
*item
)
629 gchar
*sanitized_uid
= NULL
;
630 gchar
*headers
= NULL
;
631 gchar
*lines
, *body
, *tmpfile
;
633 sanitized_uid
= g_strdup(uid
);
634 subst_for_filename(sanitized_uid
);
636 tmpfile
= g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
637 G_DIR_SEPARATOR
, getuid(), sanitized_uid
);
638 g_free(sanitized_uid
);
640 headers
= write_headers_date(uid
);
643 g_warning("can't get headers");
648 if (!strcmp(uid
, EVENT_PAST_ID
))
650 else if (!strcmp(uid
, EVENT_TODAY_ID
))
652 else if (!strcmp(uid
, EVENT_TOMORROW_ID
))
653 date
= EVENT_TOMORROW
;
654 else if (!strcmp(uid
, EVENT_THISWEEK_ID
))
655 date
= EVENT_THISWEEK
;
656 else if (!strcmp(uid
, EVENT_LATER_ID
))
661 lines
= get_item_event_list_for_date(item
, date
);
662 body
= g_strdup_printf("%s"
664 "%s", headers
, lines
);
666 if (str_write_to_file(body
, tmpfile
, FALSE
) < 0) {
670 chmod(tmpfile
, S_IRUSR
|S_IWUSR
);
678 static gchar
*write_headers_ical(PrefsAccount
*account
,
679 icalcomponent
*ievent
,
682 gchar
*vcal_manager_icalevent_dump(icalcomponent
*event
, gchar
*orga
, icalcomponent
*use_calendar
)
684 PrefsAccount
*account
= account_get_cur_account();
685 gchar
*body
, *headers
, *qpbody
;
686 gchar
**lines
= NULL
;
687 gchar
*tmpfile
= NULL
;
688 icalcomponent
*calendar
;
690 icalcomponent
*ievent
= NULL
;
693 ievent
= icalcomponent_new_clone(event
);
695 prop
= icalcomponent_get_first_property(ievent
, ICAL_UID_PROPERTY
);
697 gchar
*sanitized_uid
= g_strdup(icalproperty_get_uid(prop
));
699 subst_for_filename(sanitized_uid
);
701 tmpfile
= g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
702 G_DIR_SEPARATOR
, getuid(), sanitized_uid
);
703 g_free(sanitized_uid
);
704 icalproperty_free(prop
);
706 tmpfile
= g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(),
707 G_DIR_SEPARATOR
, getuid(), ievent
);
712 icalcomponent_free(ievent
);
718 if (use_calendar
!= NULL
) {
719 calendar
= use_calendar
;
725 ICAL_VCALENDAR_COMPONENT
,
726 icalproperty_new_version("2.0"),
727 icalproperty_new_prodid(
728 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
729 icalproperty_new_calscale("GREGORIAN"),
730 icalproperty_new_method(ICAL_METHOD_PUBLISH
),
735 g_warning("can't generate calendar");
737 icalcomponent_free(ievent
);
741 icalcomponent_add_component(calendar
, ievent
);
746 headers
= write_headers_ical(account
, ievent
, orga
);
749 g_warning("can't get headers");
751 icalcomponent_free(calendar
);
755 lines
= g_strsplit(icalcomponent_as_ical_string(calendar
), "\n", 0);
756 qpbody
= g_strdup("");
758 /* encode to quoted-printable */
760 gint e_len
= strlen(qpbody
), n_len
= 0;
761 gchar
*outline
= conv_codeset_strdup(lines
[i
], CS_UTF_8
, conv_get_outgoing_charset_str());
762 gchar
*qpoutline
= g_malloc(strlen(outline
)*8 + 1);
764 qp_encode_line(qpoutline
, (guchar
*)outline
);
765 n_len
= strlen(qpoutline
);
767 qpbody
= g_realloc(qpbody
, e_len
+ n_len
+ 1);
768 strcpy(qpbody
+e_len
, qpoutline
);
769 *(qpbody
+n_len
+e_len
) = '\0';
776 body
= g_strdup_printf("%s"
778 "%s", headers
, qpbody
);
780 if (str_write_to_file(body
, tmpfile
, FALSE
) < 0) {
784 chmod(tmpfile
, S_IRUSR
|S_IWUSR
);
790 icalcomponent_free(calendar
);
795 VCalEvent
* vcal_manager_new_event (const gchar
*uid
,
796 const gchar
*organizer
,
797 const gchar
*orgname
,
798 const gchar
*location
,
799 const gchar
*summary
,
800 const gchar
*description
,
801 const gchar
*dtstart
,
806 icalproperty_method method
,
808 const gchar
*created
,
809 const gchar
*last_modified
,
810 icalcomponent_kind type
)
812 VCalEvent
*event
= g_new0(VCalEvent
, 1);
814 event
->uid
= g_strdup(uid
?uid
:"");
815 event
->organizer
= g_strdup(organizer
?organizer
:"");
816 event
->orgname
= g_strdup(orgname
?orgname
:"");
818 if (dtend
&& *(dtend
)) {
819 time_t tmp
= icaltime_as_timet((icaltime_from_string(dtend
)));
820 GDateTime
*dt
= g_date_time_new_from_unix_local(tmp
);
821 event
->end
= g_date_time_format(dt
, "%a, %e %b %Y %H:%M:%S %Z");
822 g_date_time_unref(dt
);
825 if (dtstart
&& *(dtstart
)) {
826 time_t tmp
= icaltime_as_timet((icaltime_from_string(dtstart
)));
827 GDateTime
*dt
= g_date_time_new_from_unix_local(tmp
);
828 event
->start
= g_date_time_format(dt
, "%a, %e %b %Y %H:%M:%S %Z");
829 g_date_time_unref(dt
);
831 event
->dtstart
= g_strdup(dtstart
?dtstart
:"");
832 event
->dtend
= g_strdup(dtend
?dtend
:"");
833 event
->recur
= g_strdup(recur
?recur
:"");
834 event
->location
= g_strdup(location
?location
:"");
835 event
->summary
= g_strdup(summary
?summary
:"");
836 event
->description
= g_strdup(description
?description
:"");
837 event
->url
= g_strdup(url
?url
:"");
838 event
->tzid
= g_strdup(tzid
?tzid
:"");
839 event
->method
= method
;
840 event
->sequence
= sequence
;
841 event
->created
= g_strdup(created
?created
:"");
842 event
->last_modified
= g_strdup(last_modified
?last_modified
:"");
844 event
->rec_occurrence
= FALSE
;
845 while (strchr(event
->summary
, '\n'))
846 *(strchr(event
->summary
, '\n')) = ' ';
851 void vcal_manager_free_event (VCalEvent
*event
)
858 g_free(event
->organizer
);
859 g_free(event
->orgname
);
860 g_free(event
->start
);
862 g_free(event
->location
);
863 g_free(event
->summary
);
864 g_free(event
->dtstart
);
865 g_free(event
->dtend
);
866 g_free(event
->recur
);
868 g_free(event
->description
);
870 for (cur
= event
->answers
; cur
; cur
= cur
->next
) {
871 answer_free((Answer
*)cur
->data
);
873 g_slist_free(event
->answers
);
877 gchar
*vcal_manager_get_event_path(void)
879 static gchar
*event_path
= NULL
;
881 event_path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
887 gchar
*vcal_manager_get_event_file(const gchar
*uid
)
889 gchar
*tmp
= g_strdup(uid
);
892 subst_for_filename(tmp
);
893 res
= g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S
,
899 PrefsAccount
*vcal_manager_get_account_from_event(VCalEvent
*event
)
901 GSList
*list
= vcal_manager_get_answers_emails(event
);
904 /* find an attendee corresponding to one of our accounts */
905 while (cur
&& cur
->data
) {
906 gchar
*email
= (gchar
*)cur
->data
;
907 if (account_find_from_address(email
, FALSE
)) {
909 return account_find_from_address(email
, FALSE
);
917 void vcal_manager_save_event (VCalEvent
*event
, gboolean export_after
)
920 XMLNode
*xmlnode
= NULL
;
921 GNode
*rootnode
= NULL
;
924 GSList
*answers
= event
->answers
;
926 gint tmp_method
= event
->method
;
928 tag
= xml_tag_new("event");
929 xml_tag_add_attr(tag
, xml_attr_new("organizer", event
->organizer
));
930 xml_tag_add_attr(tag
, xml_attr_new("orgname", event
->orgname
));
931 xml_tag_add_attr(tag
, xml_attr_new("location", event
->location
));
932 xml_tag_add_attr(tag
, xml_attr_new("summary", event
->summary
));
933 xml_tag_add_attr(tag
, xml_attr_new("description", event
->description
));
934 xml_tag_add_attr(tag
, xml_attr_new("url", event
->url
));
935 xml_tag_add_attr(tag
, xml_attr_new("dtstart", event
->dtstart
));
936 xml_tag_add_attr(tag
, xml_attr_new("dtend", event
->dtend
));
937 xml_tag_add_attr(tag
, xml_attr_new("recur", event
->recur
));
938 xml_tag_add_attr(tag
, xml_attr_new("tzid", event
->tzid
));
940 /* updating answers saves events, don't save them with reply type */
941 if (tmp_method
== ICAL_METHOD_REPLY
)
942 tmp_method
= ICAL_METHOD_REQUEST
;
944 tmp
= g_strdup_printf("%d", tmp_method
);
945 xml_tag_add_attr(tag
, xml_attr_new("method", tmp
));
948 tmp
= g_strdup_printf("%d", event
->sequence
);
949 xml_tag_add_attr(tag
, xml_attr_new("sequence", tmp
));
950 xml_tag_add_attr(tag
, xml_attr_new("created", event
->created
));
951 xml_tag_add_attr(tag
, xml_attr_new("last_modified", event
->last_modified
));
954 tmp
= g_strdup_printf("%d", event
->type
);
955 xml_tag_add_attr(tag
, xml_attr_new("type", tmp
));
958 tmp
= g_strdup_printf("%"CM_TIME_FORMAT
, event
->postponed
);
959 xml_tag_add_attr(tag
, xml_attr_new("postponed", tmp
));
962 tmp
= g_strdup_printf("%d", event
->rec_occurrence
);
963 xml_tag_add_attr(tag
, xml_attr_new("rec_occurrence", tmp
));
966 xmlnode
= xml_node_new(tag
, NULL
);
967 rootnode
= g_node_new(xmlnode
);
969 while (answers
&& answers
->data
) {
970 XMLNode
*ansxmlnode
= NULL
;
971 GNode
*ansnode
= NULL
;
972 XMLTag
*anstag
= xml_tag_new("answer");
973 Answer
*a
= (Answer
*)answers
->data
;
974 xml_tag_add_attr(anstag
, xml_attr_new("attendee", a
->attendee
));
975 xml_tag_add_attr(anstag
, xml_attr_new("name", a
->name
?a
->name
:""));
976 tmp
= g_strdup_printf("%d", a
->answer
);
977 xml_tag_add_attr(anstag
, xml_attr_new("answer", tmp
));
979 tmp
= g_strdup_printf("%d", a
->cutype
);
980 xml_tag_add_attr(anstag
, xml_attr_new("cutype", tmp
));
982 ansxmlnode
= xml_node_new(anstag
, NULL
);
983 ansnode
= g_node_new(ansxmlnode
);
984 g_node_append(rootnode
, ansnode
);
985 answers
= answers
->next
;
988 path
= vcal_manager_get_event_file(event
->uid
);
990 if ((pfile
= prefs_write_open(path
)) == NULL
) {
991 gchar
*dir_path
= vcal_manager_get_event_path();
992 if (!is_dir_exist(dir_path
) && make_dir(vcal_manager_get_event_path()) != 0) {
998 if ((pfile
= prefs_write_open(path
)) == NULL
) {
1005 xml_file_put_xml_decl(pfile
->fp
);
1006 xml_write_tree(rootnode
, pfile
->fp
);
1007 xml_free_tree(rootnode
);
1009 if (prefs_file_close(pfile
) < 0) {
1010 g_warning("failed to write event");
1015 vcal_folder_export(NULL
);
1018 static VCalEvent
*event_get_from_xml (const gchar
*uid
, GNode
*node
)
1022 gchar
*org
= NULL
, *location
= NULL
, *summary
= NULL
, *orgname
= NULL
;
1023 gchar
*dtstart
= NULL
, *dtend
= NULL
, *tzid
= NULL
;
1024 gchar
*created
= NULL
, *last_modified
= NULL
;
1025 gchar
*description
= NULL
, *url
= NULL
, *recur
= NULL
;
1026 VCalEvent
*event
= NULL
;
1027 icalproperty_method method
= ICAL_METHOD_REQUEST
;
1028 icalcomponent_kind type
= ICAL_VEVENT_COMPONENT
;
1029 gint sequence
= 0, rec_occurrence
= 0;
1030 time_t postponed
= (time_t)0;
1032 g_return_val_if_fail(node
->data
!= NULL
, NULL
);
1034 xmlnode
= node
->data
;
1035 if (g_strcmp0(xmlnode
->tag
->tag
, "event") != 0) {
1036 g_warning("tag name != \"event\"");
1040 list
= xmlnode
->tag
->attr
;
1041 for (; list
!= NULL
; list
= list
->next
) {
1042 XMLAttr
*attr
= list
->data
;
1044 if (!attr
|| !attr
->name
|| !attr
->value
) continue;
1045 if (!strcmp(attr
->name
, "organizer"))
1046 org
= g_strdup(attr
->value
);
1047 if (!strcmp(attr
->name
, "orgname"))
1048 orgname
= g_strdup(attr
->value
);
1049 if (!strcmp(attr
->name
, "location"))
1050 location
= g_strdup(attr
->value
);
1051 if (!strcmp(attr
->name
, "summary"))
1052 summary
= g_strdup(attr
->value
);
1053 if (!strcmp(attr
->name
, "description"))
1054 description
= g_strdup(attr
->value
);
1055 if (!strcmp(attr
->name
, "url"))
1056 url
= g_strdup(attr
->value
);
1057 if (!strcmp(attr
->name
, "dtstart"))
1058 dtstart
= g_strdup(attr
->value
);
1059 if (!strcmp(attr
->name
, "dtend"))
1060 dtend
= g_strdup(attr
->value
);
1061 if (!strcmp(attr
->name
, "recur"))
1062 recur
= g_strdup(attr
->value
);
1063 if (!strcmp(attr
->name
, "tzid"))
1064 tzid
= g_strdup(attr
->value
);
1065 if (!strcmp(attr
->name
, "type"))
1066 type
= atoi(attr
->value
);
1067 if (!strcmp(attr
->name
, "method"))
1068 method
= atoi(attr
->value
);
1069 if (!strcmp(attr
->name
, "sequence"))
1070 sequence
= atoi(attr
->value
);
1071 if (!strcmp(attr
->name
, "created"))
1072 created
= g_strdup(attr
->value
);
1073 if (!strcmp(attr
->name
, "last_modified"))
1074 last_modified
= g_strdup(attr
->value
);
1075 if (!strcmp(attr
->name
, "postponed"))
1076 postponed
= atoi(attr
->value
);
1077 if (!strcmp(attr
->name
, "rec_occurrence"))
1078 rec_occurrence
= atoi(attr
->value
);
1081 event
= vcal_manager_new_event(uid
, org
, orgname
, location
, summary
, description
,
1082 dtstart
, dtend
, recur
, tzid
, url
, method
,
1083 sequence
, created
, last_modified
, type
);
1085 event
->postponed
= postponed
;
1086 event
->rec_occurrence
= rec_occurrence
;
1092 g_free(description
);
1099 g_free(last_modified
);
1101 node
= node
->children
;
1102 while (node
!= NULL
) {
1103 gchar
*attendee
= NULL
;
1105 icalparameter_partstat answer
= ICAL_PARTSTAT_NEEDSACTION
;
1106 icalparameter_cutype cutype
= ICAL_CUTYPE_INDIVIDUAL
;
1108 xmlnode
= node
->data
;
1109 if (g_strcmp0(xmlnode
->tag
->tag
, "answer") != 0) {
1110 g_warning("tag name != \"answer\"");
1113 list
= xmlnode
->tag
->attr
;
1114 for (; list
!= NULL
; list
= list
->next
) {
1115 XMLAttr
*attr
= list
->data
;
1117 if (!attr
|| !attr
->name
|| !attr
->value
) continue;
1118 if (!strcmp(attr
->name
, "attendee"))
1119 attendee
= g_strdup(attr
->value
);
1120 if (!strcmp(attr
->name
, "name"))
1121 name
= g_strdup(attr
->value
);
1122 if (!strcmp(attr
->name
, "answer"))
1123 answer
= atoi(attr
->value
);
1124 if (!strcmp(attr
->name
, "cutype"))
1125 cutype
= atoi(attr
->value
);
1128 event
->answers
= g_slist_prepend(event
->answers
, answer_new(attendee
, name
, answer
, cutype
));
1133 event
->answers
= g_slist_reverse(event
->answers
);
1138 VCalEvent
*vcal_manager_load_event (const gchar
*uid
)
1142 VCalEvent
*event
= NULL
;
1144 path
= vcal_manager_get_event_file(uid
);
1146 if (!is_file_exist(path
)) {
1151 node
= xml_parse_file(path
);
1156 g_warning("no node");
1160 event
= event_get_from_xml(uid
, node
);
1161 /* vcal_manager_event_print(event); */
1162 xml_free_tree(node
);
1167 while (strchr(event
->summary
, '\n'))
1168 *(strchr(event
->summary
, '\n')) = ' ';
1174 void vcal_manager_update_answer (VCalEvent
*event
,
1175 const gchar
*attendee
,
1177 icalparameter_partstat ans
,
1178 icalparameter_cutype cutype
)
1180 Answer
*answer
= NULL
;
1181 GSList
*existing
= NULL
;
1182 Answer
*existing_a
= NULL
;
1187 answer
= answer_new(attendee
, name
, ans
, cutype
);
1188 existing
= answer_find(event
, answer
);
1191 existing_a
= (Answer
*)existing
->data
;
1193 if (!answer
->name
&& existing_a
->name
)
1194 answer
->name
= g_strdup(existing_a
->name
);
1195 if (!answer
->cutype
&& existing_a
->cutype
)
1196 answer
->cutype
= existing_a
->cutype
;
1198 answer_remove(event
, answer
);
1201 answer_add(event
, answer
);
1203 vcal_manager_save_event(event
, FALSE
);
1206 static gchar
*write_headers(PrefsAccount
*account
,
1208 gboolean short_headers
,
1210 gboolean is_pseudo_display
)
1212 gchar
*subject
= NULL
;
1213 gchar date
[RFC822_DATE_BUFFSIZE
];
1214 gchar
*save_folder
= NULL
;
1215 gchar
*result
= NULL
;
1216 gchar
*queue_headers
= NULL
;
1217 gchar
*method_str
= NULL
;
1218 gchar
*attendees
= NULL
;
1219 icalparameter_partstat status
;
1220 gchar
*prefix
= NULL
;
1221 gchar enc_subject
[512], enc_from
[512], *from
= NULL
;
1223 gchar
*calmsgid
= NULL
;
1225 cm_return_val_if_fail(account
!= NULL
, NULL
);
1227 memset(date
, 0, sizeof(date
));
1229 if (is_pseudo_display
) {
1230 struct icaltimetype itt
= (icaltime_from_string(event
->dtstart
));
1231 time_t t
= icaltime_as_timet(itt
);
1232 get_rfc822_date_from_time_t(date
, sizeof(date
), t
);
1234 get_rfc822_date(date
, sizeof(date
));
1237 if (account_get_special_folder(account
, F_OUTBOX
)) {
1238 save_folder
= folder_item_get_identifier(account_get_special_folder
1239 (account
, F_OUTBOX
));
1243 GSList
*cur
= event
->answers
;
1244 while (cur
&& cur
->data
) {
1246 Answer
*a
= (Answer
*)cur
->data
;
1248 if (strcasecmp(a
->attendee
, event
->organizer
)) {
1250 tmp
= g_strdup_printf("%s>,\n <%s", attendees
, a
->attendee
);
1254 attendees
= g_strdup_printf("%s", a
->attendee
);
1261 if (!short_headers
) {
1262 queue_headers
= g_strdup_printf("S:%s\n"
1267 "X-Claws-End-Special-Headers: 1\n",
1269 account
->smtp_server
,
1270 is_reply
? event
->organizer
:attendees
,
1271 account
->account_id
,
1272 save_folder
?"SCF:":"",
1273 save_folder
?save_folder
:"",
1274 save_folder
?"\n":"");
1276 queue_headers
= g_strdup("");
1281 method_str
= "REPLY";
1282 status
= vcal_manager_get_reply_for_attendee(event
, account
->address
);
1283 if (status
== ICAL_PARTSTAT_ACCEPTED
)
1284 prefix
= _("Accepted: ");
1285 else if (status
== ICAL_PARTSTAT_DECLINED
)
1286 prefix
= _("Declined: ");
1287 else if (status
== ICAL_PARTSTAT_TENTATIVE
)
1288 prefix
= _("Tentatively Accepted: ");
1291 } else if (event
->method
== ICAL_METHOD_PUBLISH
) {
1292 method_str
= "PUBLISH";
1293 } else if (event
->method
== ICAL_METHOD_CANCEL
) {
1294 method_str
= "CANCEL";
1296 method_str
= "REQUEST";
1299 subject
= g_strdup_printf("%s%s", prefix
, event
->summary
);
1301 conv_encode_header_full(enc_subject
, sizeof(enc_subject
), subject
, strlen("Subject: "),
1302 FALSE
, conv_get_outgoing_charset_str());
1303 from
= is_reply
?account
->name
:(event
->orgname
?event
->orgname
:"");
1304 conv_encode_header_full(enc_from
, sizeof(enc_from
), from
, strlen("From: "),
1305 TRUE
, conv_get_outgoing_charset_str());
1307 if (is_pseudo_display
&& event
->uid
) {
1308 calmsgid
= g_strdup_printf("Message-ID: <%s>\n",event
->uid
);
1310 calmsgid
= g_strdup("");
1313 msgid
= prefs_account_generate_msgid(account
);
1315 result
= g_strdup_printf("%s"
1320 "MIME-Version: 1.0\n"
1321 "Content-Type: text/calendar; method=%s; charset=\"%s\"\n"
1322 "Content-Transfer-Encoding: 8bit\n"
1327 is_reply
? account
->address
:event
->organizer
,
1328 is_reply
? event
->organizer
:(attendees
?attendees
:event
->organizer
),
1335 "In-Reply-To":"Message-ID",
1337 event_to_today_str(event
, 0):msgid
);
1340 g_free(save_folder
);
1341 g_free(queue_headers
);
1349 static gchar
*write_headers_ical(PrefsAccount
*account
,
1350 icalcomponent
*ievent
,
1354 gchar date
[RFC822_DATE_BUFFSIZE
];
1355 gchar
*result
= NULL
;
1356 gchar
*method_str
= NULL
;
1357 gchar
*summary
= NULL
;
1358 gchar
*organizer
= NULL
;
1359 gchar
*orgname
= NULL
;
1360 icalproperty
*prop
= NULL
;
1361 gchar
*calmsgid
= NULL
;
1363 time_t t
= (time_t)0;
1365 memset(subject
, 0, sizeof(subject
));
1366 memset(date
, 0, sizeof(date
));
1368 prop
= icalcomponent_get_first_property(ievent
, ICAL_SUMMARY_PROPERTY
);
1369 summary
= g_strdup(icalproperty_get_summary(prop
));
1370 icalproperty_free(prop
);
1372 summary
= g_strdup(_("[no summary]"));
1374 while (strchr(summary
, '\n'))
1375 *(strchr(summary
, '\n')) = ' ';
1377 prop
= icalcomponent_get_first_property(ievent
, ICAL_ORGANIZER_PROPERTY
);
1379 organizer
= g_strdup(icalproperty_get_organizer(prop
));
1380 if (icalproperty_get_parameter_as_string(prop
, "CN") != NULL
)
1381 orgname
= g_strdup(icalproperty_get_parameter_as_string(prop
, "CN"));
1383 icalproperty_free(prop
);
1385 organizer
= orga
? g_strdup(orga
):g_strdup("");
1388 prop
= icalcomponent_get_first_property(ievent
, ICAL_DTSTART_PROPERTY
);
1390 t
= icaltime_as_timet(icalproperty_get_dtstart(prop
));
1391 get_rfc822_date_from_time_t(date
, sizeof(date
), t
);
1393 get_rfc822_date(date
, sizeof(date
));
1396 conv_encode_header(subject
, 511, summary
, strlen("Subject: "), FALSE
);
1398 method_str
= "PUBLISH";
1400 prop
= icalcomponent_get_first_property(ievent
, ICAL_UID_PROPERTY
);
1402 calmsgid
= g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop
));
1403 icalproperty_free(prop
);
1405 calmsgid
= g_strdup("");
1409 result
= g_strdup_printf("From: %s <%s>\n"
1413 "MIME-Version: 1.0\n"
1414 "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n"
1415 "Content-Transfer-Encoding: quoted-printable\n"
1417 "In-Reply-To: <%s>\n",
1419 !strncmp(organizer
, "MAILTO:", 7) ? organizer
+7 : organizer
,
1425 conv_get_outgoing_charset_str(),
1427 event_to_today_str(NULL
, t
));
1439 static gboolean
vcal_manager_send (PrefsAccount
*account
,
1443 gchar
*tmpfile
= NULL
;
1445 FolderItem
*folderitem
;
1446 gchar
*msgpath
= NULL
;
1447 Folder
*folder
= NULL
;
1449 tmpfile
= vcal_manager_event_dump(event
, is_reply
, FALSE
, NULL
, TRUE
);
1454 folderitem
= account_get_special_folder(account
, F_QUEUE
);
1456 g_warning("can't find queue folder for %s", account
->address
);
1461 folder_item_scan(folderitem
);
1463 if ((msgnum
= folder_item_add_msg(folderitem
, tmpfile
, NULL
, TRUE
)) < 0) {
1464 g_warning("can't queue the message");
1470 msgpath
= folder_item_fetch_msg(folderitem
, msgnum
);
1472 if (!prefs_common_get_prefs()->work_offline
) {
1474 gboolean queued_removed
= FALSE
;
1475 gint val
= procmsg_send_message_queue_with_lock(msgpath
, &err
, folderitem
, msgnum
, &queued_removed
);
1477 if (!queued_removed
)
1478 folder_item_remove_msg(folderitem
, msgnum
);
1479 folder_item_scan(folderitem
);
1481 alertpanel_error_log("%s", err
);
1489 folder
= folder_find_from_name ("vCalendar", vcal_folder_get_class());
1491 folder_item_scan(folder
->inbox
);
1492 vcalviewer_reload(folder
->inbox
);
1494 g_warning("couldn't find vCalendar folder class");
1498 gboolean
vcal_manager_reply (PrefsAccount
*account
,
1501 return vcal_manager_send(account
, event
, TRUE
);
1504 gboolean
vcal_manager_request (PrefsAccount
*account
,
1507 return vcal_manager_send(account
, event
, FALSE
);
1510 EventTime
event_to_today(VCalEvent
*event
, time_t t
)
1512 struct tm evtstart
, today
;
1513 time_t evtstart_t
, today_t
;
1514 struct icaltimetype itt
;
1518 today_t
= time(NULL
);
1520 itt
= icaltime_from_string(event
->dtstart
);
1521 evtstart_t
= icaltime_as_timet(itt
);
1528 today
= *localtime_r(&today_t
, &buft
);
1529 localtime_r(&evtstart_t
, &evtstart
);
1535 today
= *localtime(&today_t
);
1536 evtstart
= *localtime(&evtstart_t
);
1539 if (today
.tm_year
== evtstart
.tm_year
) {
1540 int days
= evtstart
.tm_yday
- today
.tm_yday
;
1543 } else if (days
== 0) {
1545 } else if (days
== 1) {
1546 return EVENT_TOMORROW
;
1547 } else if (days
> 1 && days
< 7) {
1548 return EVENT_THISWEEK
;
1552 } else if (today
.tm_year
> evtstart
.tm_year
) {
1554 } else if (today
.tm_year
== evtstart
.tm_year
- 1) {
1555 int days
= ((365 - today
.tm_yday
) + evtstart
.tm_yday
);
1558 } else if (days
== 1) {
1559 return EVENT_TOMORROW
;
1560 } else if (days
> 1 && days
< 7) {
1561 return EVENT_THISWEEK
;
1569 const gchar
*event_to_today_str(VCalEvent
*event
, time_t t
)
1571 EventTime days
= event_to_today(event
, t
);
1574 return EVENT_PAST_ID
;
1576 return EVENT_TODAY_ID
;
1577 case EVENT_TOMORROW
:
1578 return EVENT_TOMORROW_ID
;
1579 case EVENT_THISWEEK
:
1580 return EVENT_THISWEEK_ID
;
1582 return EVENT_LATER_ID
;