Code cleanup around glib version check (2.28 minimum).
[claws.git] / src / plugins / vcalendar / vcal_manager.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2017 Colin Leroy <colin@colino.net> and 
4  * the Claws Mail team
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <stddef.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <libical/ical.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"
41 #include "xml.h"
42 #include "xmlprops.h"
43 #include "prefs.h"
44 #include "prefs_common.h"
45 #include "prefs_account.h"
46 #include "alertpanel.h"
47 #include "account.h"
48 #include "codeconv.h"
49 #include <time.h>
50 #include "folder.h"
51 #include "quoted-printable.h"
52 #include "utils.h"
53 #include "defs.h"
54
55 #ifdef G_OS_WIN32
56 #define getuid() 0
57 #endif
58
59 Answer *answer_new(const gchar *attendee, 
60                           const gchar *name,
61                           icalparameter_partstat ans,
62                           icalparameter_cutype cutype)
63 {
64         Answer *answer = g_new0(Answer, 1);
65         answer->attendee = g_strdup(attendee);
66         answer->name = g_strdup(name);
67         if (!answer->name)
68                 answer->name = g_strdup("");
69         if (!answer->attendee)
70                 answer->attendee = g_strdup("");
71         answer->answer = ans;
72         answer->cutype = cutype;
73         return answer;
74 }
75
76 static void answer_free(Answer *answer)
77 {
78         g_free(answer->attendee);
79         g_free(answer->name);
80         g_free(answer);
81 }
82
83 static GSList *answer_find(VCalEvent *event, Answer *answer)
84 {
85         GSList *cur = event->answers;
86         while (cur && cur->data) {
87                 Answer *b = (Answer *)cur->data;
88                 if (!strcasecmp(b->attendee, answer->attendee))
89                         return cur;
90                 cur = cur->next;
91         }
92         return NULL;
93 }
94
95 void vcal_manager_copy_attendees(VCalEvent *src, VCalEvent *dest)
96 {
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);
102                 cur = cur->next;
103         }
104         dest->answers = g_slist_reverse(dest->answers);
105 }
106
107 gchar *vcal_manager_answer_get_text(icalparameter_partstat ans) 
108 {
109         static gchar *replies[5]={
110                 N_("accepted"),
111                 N_("tentatively accepted"),
112                 N_("declined"),
113                 N_("did not answer"),
114                 N_("unknown")
115         };
116
117         switch (ans) {
118         case ICAL_PARTSTAT_ACCEPTED:
119                 return _(replies[0]);
120                 break;
121         case ICAL_PARTSTAT_DECLINED:
122                 return _(replies[2]);
123                 break;
124         case ICAL_PARTSTAT_TENTATIVE:
125                 return _(replies[1]);
126                 break;
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]);
136                 break;                  
137         }
138         return NULL;
139 }
140
141 gchar *vcal_manager_cutype_get_text(icalparameter_cutype type) 
142 {
143         static gchar *replies[5]={
144                 N_("individual"),
145                 N_("group"),
146                 N_("resource"),
147                 N_("room"),
148                 N_("unknown")
149         };
150
151         switch (type) {
152         case ICAL_CUTYPE_INDIVIDUAL:
153                 return _(replies[0]);
154                 break;
155         case ICAL_CUTYPE_GROUP:
156                 return _(replies[1]);
157                 break;
158         case ICAL_CUTYPE_RESOURCE:
159                 return _(replies[2]);
160                 break;
161         case ICAL_CUTYPE_ROOM:
162                 return _(replies[3]);
163         default:
164                 return _(replies[4]);
165                 break;                  
166         }
167         return NULL;
168 }
169
170 static GSList *answer_remove(VCalEvent *event, Answer *answer)
171 {
172         GSList *cur = answer_find(event, answer);
173         if (cur) {
174                 Answer *b = (Answer *)cur->data;
175                 event->answers = g_slist_remove(event->answers, b);
176                 answer_free(b);
177         }
178         return event->answers;
179 }
180
181 static GSList *answer_add(VCalEvent *event, Answer *answer)
182 {
183         event->answers = g_slist_append(event->answers, answer);
184         return event->answers;
185 }
186
187 GSList *vcal_manager_get_answers_emails(VCalEvent *event)
188 {
189         GSList *new = NULL;
190         GSList *cur = event->answers;
191         while (cur && cur->data) {
192                 Answer *b = (Answer *)cur->data;
193                 new = g_slist_prepend(new, b->attendee);
194                 cur = cur->next;
195         }
196         new = g_slist_reverse(new);
197         return new;     
198 }
199
200 icalparameter_partstat vcal_manager_get_reply_for_attendee(VCalEvent *event, const gchar *att)
201 {
202         Answer *a = answer_new(att, NULL, 0, 0);
203         GSList *ans = answer_find(event, a);
204         icalparameter_partstat res = 0;
205         if (ans) {
206                 Answer *b = (Answer *)ans->data;
207                 res = b->answer;
208         }
209         answer_free(a);
210         return res;
211 }
212
213 gchar *vcal_manager_get_cutype_text_for_attendee(VCalEvent *event, const gchar *att)
214 {
215         icalparameter_cutype status = vcal_manager_get_cutype_for_attendee(event, att);
216         gchar *res = NULL;
217         if (status != 0) 
218                 res = g_strdup(vcal_manager_cutype_get_text(status));
219
220         return res;
221 }
222
223 icalparameter_partstat vcal_manager_get_cutype_for_attendee(VCalEvent *event, const gchar *att)
224 {
225         Answer *a = answer_new(att, NULL, 0, 0);
226         GSList *ans = answer_find(event, a);
227         icalparameter_cutype res = 0;
228         if (ans) {
229                 Answer *b = (Answer *)ans->data;
230                 res = b->cutype;
231         }
232         answer_free(a);
233         return res;
234 }
235
236 gchar *vcal_manager_get_reply_text_for_attendee(VCalEvent *event, const gchar *att)
237 {
238         icalparameter_partstat status = vcal_manager_get_reply_for_attendee(event, att);
239         gchar *res = NULL;
240         if (status != 0) 
241                 res = g_strdup(vcal_manager_answer_get_text(status));
242
243         return res;
244 }
245
246 gchar *vcal_manager_get_attendee_name(VCalEvent *event, const gchar *att)
247 {
248         Answer *a = answer_new(att, NULL, 0, 0);
249         GSList *ans = answer_find(event, a);
250         gchar *res = NULL;
251         if (ans) {
252                 Answer *b = (Answer *)ans->data;
253                 if (b->name)
254                         res = g_strdup(b->name);
255         }
256         answer_free(a);
257         return res;
258 }
259
260 void vcal_manager_event_print(VCalEvent *event)
261 {
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"
266                 "event->end\t\t%s\n"
267                 "event->location\t\t%s\n"
268                 "event->summary\t\t%s\n"
269                 "event->description\t%s\n"
270                 "event->url\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",
277                 event->uid,
278                 event->organizer,
279                 event->start,
280                 event->end,
281                 event->location,
282                 event->summary,
283                 event->description,
284                 event->url,
285                 event->dtstart,
286                 event->dtend,
287                 event->recur,
288                 event->tzid,
289                 event->method,
290                 event->sequence);
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));
294                 list = list->next;
295         }
296         
297 }
298
299 static gchar *write_headers(PrefsAccount        *account, 
300                             VCalEvent           *event,
301                             gboolean             short_headers,
302                             gboolean             is_reply, 
303                             gboolean             is_pseudo_event);
304
305 gchar *vcal_manager_event_dump(VCalEvent *event, gboolean is_reply, gboolean is_pseudo_event,
306                                 icalcomponent *use_calendar, gboolean modif)
307 {
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);
318         
319         subst_for_filename(sanitized_uid);
320
321         tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
322                                       G_DIR_SEPARATOR, getuid(), sanitized_uid);
323         g_free(sanitized_uid);
324
325         if (!account) {
326                 g_free(organizer);
327                 g_free(tmpfile);
328                 debug_print("no account found\n");
329                 return NULL;
330         }
331
332         attendee = g_strdup_printf("MAILTO:%s", account->address);
333         
334         if (vcal_manager_get_reply_for_attendee(event, account->address) != 0)
335                 status = vcal_manager_get_reply_for_attendee(event, account->address);
336         
337         tzset();
338         
339         if (use_calendar != NULL) {
340                 calendar = use_calendar;
341                 g_free(tmpfile);
342                 tmpfile = NULL;
343         } else
344                 calendar = 
345                         icalcomponent_vanew(
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),
352                             (void*)0
353                             );  
354
355         if (!calendar) {
356                 g_warning ("can't generate calendar");
357                 g_free(organizer);
358                 g_free(tmpfile);
359                 g_free(attendee);
360                 return NULL;
361         }
362
363         orgprop = icalproperty_new_organizer(organizer);
364         
365         timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
366         
367         icalcomponent_add_property(timezone,
368                 icalproperty_new_tzid("UTC")); /* free */
369         
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"));
380
381         icalcomponent_add_component(timezone, tzc);
382
383         ievent = 
384             icalcomponent_vanew(
385                 ICAL_VEVENT_COMPONENT, (void*)0);
386
387         if (!ievent) {
388                 g_warning ("can't generate event");
389                 g_free(organizer);
390                 g_free(tmpfile);
391                 g_free(attendee);
392                 return NULL;
393         }
394         
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));
406         }
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));
417         if (event->location && *event->location)
418                 icalcomponent_add_property(ievent,
419                         icalproperty_new_location(event->location));
420         else
421                 icalcomponent_add_property(ievent,
422                         icalproperty_new_location(""));
423         icalcomponent_add_property(ievent,
424                 icalproperty_new_status(ICAL_STATUS_CONFIRMED));
425         icalcomponent_add_property(ievent,
426                 icalproperty_vanew_created(icaltime_from_timet_with_zone(time(NULL), TRUE, NULL), (void*)0));
427         icalcomponent_add_property(ievent,
428                 icalproperty_vanew_lastmodified(icaltime_from_timet_with_zone(time(NULL), TRUE, NULL), (void*)0));
429         icalcomponent_add_property(ievent,              
430                 orgprop);
431
432         if (!icalcomponent_get_first_component(calendar, ICAL_VTIMEZONE_COMPONENT))
433                 icalcomponent_add_component(calendar, timezone);
434         else 
435                 icalcomponent_free(timezone);
436
437         icalcomponent_add_component(calendar, ievent);
438
439         if (event->url && *(event->url)) {
440                 attprop = icalproperty_new_url(event->url);
441                 icalcomponent_add_property(ievent, attprop);
442         }
443
444         if (is_reply) {
445                 /* dump only this attendee */
446                 attprop =
447                         icalproperty_vanew_attendee(
448                             attendee,
449                             icalparameter_new_role(
450                                 ICAL_ROLE_REQPARTICIPANT),
451                             icalparameter_new_rsvp(ICAL_RSVP_TRUE),
452                             icalparameter_new_partstat(status),
453                             (void*)0
454                             );
455                 icalcomponent_add_property(ievent, attprop);
456         } else {
457                 /* dump all attendees */
458                 GSList *cur = event->answers;
459                 while (cur && cur->data) {
460                         Answer *a = (Answer *)cur->data;
461
462                         if (a->cutype == 0)
463                                 a->cutype = ICAL_CUTYPE_INDIVIDUAL;
464                         if (a->answer == 0)
465                                 a->answer = ICAL_PARTSTAT_NEEDSACTION;
466
467                         attprop =
468                                 icalproperty_vanew_attendee(
469                                     a->attendee,
470                                     icalparameter_new_role(
471                                         ICAL_ROLE_REQPARTICIPANT),
472                                     icalparameter_new_rsvp(ICAL_RSVP_TRUE),
473                                     icalparameter_new_cutype(a->cutype),
474                                     icalparameter_new_partstat(a->answer),
475                                     (void*)0
476                                     );
477
478                         icalcomponent_add_property(ievent, attprop);
479                         cur = cur->next;
480                 }
481         }
482
483         if (use_calendar) {
484                 g_free(organizer);
485                 g_free(tmpfile);
486                 g_free(attendee);
487                 return NULL;
488         }
489
490         headers = write_headers(account, event, is_pseudo_event, is_reply, is_pseudo_event);
491
492         if (!headers) {
493                 g_warning("can't get headers");
494                 g_free(organizer);
495                 g_free(tmpfile);
496                 g_free(attendee);
497                 return NULL;
498         }
499
500         body = g_strdup_printf("%s"
501                                "\n"
502                                "%s", headers, icalcomponent_as_ical_string(calendar));
503         
504         if (str_write_to_file(body, tmpfile) < 0) {
505                 g_free(tmpfile);
506                 tmpfile = NULL;
507         }
508
509         chmod(tmpfile, S_IRUSR|S_IWUSR);
510
511         g_free(body);
512         g_free(headers);
513         icalcomponent_free(calendar);
514         g_free(attendee);
515         g_free(organizer);
516         
517         tzset();
518
519         return tmpfile; 
520 }
521
522 static void get_rfc822_date_from_time_t(gchar *buf, gint len, time_t t)
523 {
524 #ifndef G_OS_WIN32
525         struct tm *lt;
526         gchar day[4], mon[4];
527         gint dd, hh, mm, ss, yyyy;
528         gchar buft1[512];
529         struct tm buft2;
530
531         lt = localtime_r(&t, &buft2);
532         if (sscanf(asctime_r(lt, buft1), "%3s %3s %d %d:%d:%d %d\n",
533                day, mon, &dd, &hh, &mm, &ss, &yyyy) != 7)
534                 g_warning("failed reading date/time");
535         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
536                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
537 #else
538         GDateTime *dt = g_date_time_new_from_unix_local(t);
539         gchar *buf2 = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
540         g_date_time_unref(dt);
541         strncpy(buf, buf2, len);
542         g_free(buf2);
543 #endif
544 }
545
546 static gchar *write_headers_date(const gchar *uid)
547 {
548         gchar subject[512];
549         gchar *t_subject;
550         gchar date[RFC822_DATE_BUFFSIZE];
551         time_t t;
552         struct tm lt;
553
554         memset(subject, 0, sizeof(subject));
555         memset(date, 0, sizeof(date));
556         
557         if (!strcmp(uid, EVENT_PAST_ID)) {
558                 t = 1;
559                 t_subject = _("Past");
560         } else if (!strcmp(uid, EVENT_TODAY_ID)) {
561                 t = time(NULL);
562                 t_subject = _("Today");
563         }  else if (!strcmp(uid, EVENT_TOMORROW_ID)) {
564                 t = time(NULL) + 86400;
565                 t_subject = _("Tomorrow");
566         }  else if (!strcmp(uid, EVENT_THISWEEK_ID)) {
567                 t = time(NULL) + (86400*2);
568                 t_subject = _("This week");
569         }  else if (!strcmp(uid, EVENT_LATER_ID)) {
570                 t = time(NULL) + (86400*7);
571                 t_subject = _("Later");
572         }  else {
573                 g_warning("unknown spec date");
574                 return NULL;
575         } 
576         
577 #ifndef G_OS_WIN32
578         struct tm buft;
579         lt = *localtime_r(&t, &buft);
580 #else
581         if (t < 0)
582                 t = 1;
583         lt = *localtime(&t);
584 #endif
585         lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
586         t = mktime(&lt);
587         get_rfc822_date_from_time_t(date, sizeof(date), t);
588         conv_encode_header(subject, 511, t_subject, strlen("Subject: "), FALSE);
589                                 
590         return g_strdup_printf("From: -\n"
591                                 "To: -\n"
592                                 "Subject: %s\n"
593                                 "Date: %s\n"
594                                 "MIME-Version: 1.0\n"
595                                 "Content-Type: text/plain; charset=\"UTF-8\";\n"
596                                 "Content-Transfer-Encoding: quoted-printable\n"
597                                 "Message-ID: <%s>\n",
598                                 subject,
599                                 date,
600                                 uid);
601 }
602
603 gchar *vcal_manager_dateevent_dump(const gchar *uid, FolderItem *item)
604 {
605         gchar *sanitized_uid = NULL;
606         gchar *headers = NULL;
607         gchar *lines, *body, *tmpfile;
608         EventTime date;
609         sanitized_uid = g_strdup(uid);
610         subst_for_filename(sanitized_uid);
611         
612         tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
613                                    G_DIR_SEPARATOR, getuid(), sanitized_uid);
614         g_free(sanitized_uid);
615
616         headers = write_headers_date(uid);
617
618         if (!headers) {
619                 g_warning("can't get headers");
620                 g_free(tmpfile);
621                 return NULL;
622         }
623
624         if (!strcmp(uid, EVENT_PAST_ID))
625                 date = EVENT_PAST;
626         else if (!strcmp(uid, EVENT_TODAY_ID))
627                 date = EVENT_TODAY;
628         else if (!strcmp(uid, EVENT_TOMORROW_ID))
629                 date = EVENT_TOMORROW;
630         else if (!strcmp(uid, EVENT_THISWEEK_ID))
631                 date = EVENT_THISWEEK;
632         else if (!strcmp(uid, EVENT_LATER_ID))
633                 date = EVENT_LATER;
634         else
635                 date = EVENT_PAST;
636
637         lines = get_item_event_list_for_date(item, date);
638         body = g_strdup_printf("%s"
639                                "\n"
640                                "%s", headers, lines);
641         g_free(lines);
642         if (str_write_to_file(body, tmpfile) < 0) {
643                 g_free(tmpfile);
644                 tmpfile = NULL;
645         } else
646                 chmod(tmpfile, S_IRUSR|S_IWUSR);
647
648         g_free(body);
649         g_free(headers);
650         
651         return tmpfile; 
652 }
653
654 static gchar *write_headers_ical(PrefsAccount   *account, 
655                             icalcomponent       *ievent,
656                             gchar               *orga);
657
658 gchar *vcal_manager_icalevent_dump(icalcomponent *event, gchar *orga, icalcomponent *use_calendar)
659 {
660         PrefsAccount *account = account_get_cur_account();
661         gchar *body, *headers, *qpbody;
662         gchar **lines = NULL;
663         gchar *tmpfile = NULL;
664         icalcomponent *calendar;
665         icalproperty *prop;
666         icalcomponent *ievent = NULL;
667         int i = 0;
668
669         ievent = icalcomponent_new_clone(event);
670
671         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
672         if (prop) {
673                 gchar *sanitized_uid = g_strdup(icalproperty_get_uid(prop));
674
675                 subst_for_filename(sanitized_uid);
676
677                 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
678                                               G_DIR_SEPARATOR, getuid(), sanitized_uid);
679                 g_free(sanitized_uid);
680                 icalproperty_free(prop);
681         } else {
682                 tmpfile = g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(),
683                                       G_DIR_SEPARATOR, getuid(), ievent);
684         }
685
686         if (!account) {
687                 g_free(tmpfile);
688                 icalcomponent_free(ievent);
689                 return NULL;
690         }
691
692         tzset();
693         
694         if (use_calendar != NULL) {
695                 calendar = use_calendar;
696                 g_free(tmpfile);
697                 tmpfile = NULL;
698         } else
699                 calendar = 
700                         icalcomponent_vanew(
701                             ICAL_VCALENDAR_COMPONENT,
702                             icalproperty_new_version("2.0"),
703                             icalproperty_new_prodid(
704                                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
705                             icalproperty_new_calscale("GREGORIAN"),
706                             icalproperty_new_method(ICAL_METHOD_PUBLISH),
707                             (void*)0
708                             );  
709
710         if (!calendar) {
711                 g_warning ("can't generate calendar");
712                 g_free(tmpfile);
713                 icalcomponent_free(ievent);
714                 return NULL;
715         }
716         
717         icalcomponent_add_component(calendar, ievent);
718
719         if (use_calendar)
720                 return NULL;
721
722         headers = write_headers_ical(account, ievent, orga);
723
724         if (!headers) {
725                 g_warning("can't get headers");
726                 g_free(tmpfile);
727                 icalcomponent_free(calendar);
728                 return NULL;
729         }
730
731         lines = g_strsplit(icalcomponent_as_ical_string(calendar), "\n", 0);
732         qpbody = g_strdup("");
733         
734         /* encode to quoted-printable */
735         while (lines[i]) {
736                 gint e_len = strlen(qpbody), n_len = 0;
737                 gchar *outline = conv_codeset_strdup(lines[i], CS_UTF_8, conv_get_outgoing_charset_str());
738                 gchar *qpoutline = g_malloc(strlen(outline)*8 + 1);
739                 
740                 qp_encode_line(qpoutline, (guchar *)outline);
741                 n_len = strlen(qpoutline);
742                 
743                 qpbody = g_realloc(qpbody, e_len + n_len + 1);
744                 strcpy(qpbody+e_len, qpoutline);
745                 *(qpbody+n_len+e_len) = '\0';
746                 
747                 g_free(outline);
748                 g_free(qpoutline);
749                 i++;
750         }
751         
752         body = g_strdup_printf("%s"
753                                "\n"
754                                "%s", headers, qpbody);
755         
756         if (str_write_to_file(body, tmpfile) < 0) {
757                 g_free(tmpfile);
758                 tmpfile = NULL;
759         } else
760                 chmod(tmpfile, S_IRUSR|S_IWUSR);
761
762         g_strfreev(lines);
763         g_free(body);
764         g_free(qpbody);
765         g_free(headers);
766         icalcomponent_free(calendar);
767         
768         return tmpfile; 
769 }
770
771 VCalEvent * vcal_manager_new_event      (const gchar    *uid, 
772                                          const gchar    *organizer,
773                                          const gchar    *orgname,
774                                          const gchar    *location,
775                                          const gchar    *summary,
776                                          const gchar    *description,
777                                          const gchar    *dtstart,
778                                          const gchar    *dtend,
779                                          const gchar    *recur,
780                                          const gchar    *tzid,
781                                          const gchar    *url,
782                                          icalproperty_method method,
783                                          gint            sequence,
784                                          icalcomponent_kind type)
785 {
786         VCalEvent *event = g_new0(VCalEvent, 1);
787
788         event->uid              = g_strdup(uid?uid:"");
789         event->organizer        = g_strdup(organizer?organizer:"");
790         event->orgname          = g_strdup(orgname?orgname:"");
791
792         if (dtend && *(dtend)) {
793                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtend)));
794                 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
795                 event->end = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %Z");
796                 g_date_time_unref(dt);
797         }
798         
799         if (dtstart && *(dtstart)) {
800                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtstart)));
801                 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
802                 event->start = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %Z");
803                 g_date_time_unref(dt);
804         }
805         event->dtstart          = g_strdup(dtstart?dtstart:"");
806         event->dtend            = g_strdup(dtend?dtend:"");
807         event->recur            = g_strdup(recur?recur:"");
808         event->location         = g_strdup(location?location:"");
809         event->summary          = g_strdup(summary?summary:"");
810         event->description      = g_strdup(description?description:"");
811         event->url              = g_strdup(url?url:"");
812         event->tzid             = g_strdup(tzid?tzid:"");
813         event->method           = method;
814         event->sequence         = sequence;
815         event->type             = type;
816         event->rec_occurrence           = FALSE;
817         while (strchr(event->summary, '\n'))
818                 *(strchr(event->summary, '\n')) = ' ';
819
820         return event;
821 }
822                                          
823 void vcal_manager_free_event (VCalEvent *event)
824 {
825         GSList *cur;
826         if (!event)
827                 return;
828
829         g_free(event->uid);
830         g_free(event->organizer);
831         g_free(event->orgname);
832         g_free(event->start);
833         g_free(event->end);
834         g_free(event->location);
835         g_free(event->summary);
836         g_free(event->dtstart);
837         g_free(event->dtend);
838         g_free(event->recur);
839         g_free(event->tzid);
840         g_free(event->description);
841         g_free(event->url);
842         for (cur = event->answers; cur; cur = cur->next) {
843                 answer_free((Answer *)cur->data);
844         }
845         g_slist_free(event->answers);
846         g_free(event);
847 }
848
849 gchar *vcal_manager_get_event_path(void)
850 {
851         static gchar *event_path = NULL;
852         if (!event_path)
853                 event_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
854                                         "vcalendar", NULL);
855         
856         return event_path;
857 }
858
859 gchar *vcal_manager_get_event_file(const gchar *uid)
860 {
861         gchar *tmp = g_strdup(uid);
862         gchar *res = NULL;
863
864         subst_for_filename(tmp);
865         res = g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S,
866                            tmp, NULL);
867         g_free(tmp);
868         return res;
869 }
870
871 PrefsAccount *vcal_manager_get_account_from_event(VCalEvent *event)
872 {
873         GSList *list = vcal_manager_get_answers_emails(event);
874         GSList *cur = list;
875         
876         /* find an attendee corresponding to one of our accounts */
877         while (cur && cur->data) {
878                 gchar *email = (gchar *)cur->data;
879                 if (account_find_from_address(email, FALSE)) {
880                         g_slist_free(list);
881                         return account_find_from_address(email, FALSE);
882                 }
883                 cur = cur->next;
884         }
885         g_slist_free(list);
886         return NULL;
887 }
888
889 void vcal_manager_save_event (VCalEvent *event, gboolean export_after)
890 {
891         XMLTag *tag = NULL;
892         XMLNode *xmlnode = NULL;
893         GNode *rootnode = NULL;
894         PrefFile *pfile;
895         gchar *path = NULL;
896         GSList *answers = event->answers;
897         gchar *tmp = NULL;
898         gint tmp_method = event->method;
899         
900         tag = xml_tag_new("event");
901         xml_tag_add_attr(tag, xml_attr_new("organizer", event->organizer));
902         xml_tag_add_attr(tag, xml_attr_new("orgname", event->orgname));
903         xml_tag_add_attr(tag, xml_attr_new("location", event->location));
904         xml_tag_add_attr(tag, xml_attr_new("summary", event->summary));
905         xml_tag_add_attr(tag, xml_attr_new("description", event->description));
906         xml_tag_add_attr(tag, xml_attr_new("url", event->url));
907         xml_tag_add_attr(tag, xml_attr_new("dtstart", event->dtstart));
908         xml_tag_add_attr(tag, xml_attr_new("dtend", event->dtend));
909         xml_tag_add_attr(tag, xml_attr_new("recur", event->recur));
910         xml_tag_add_attr(tag, xml_attr_new("tzid", event->tzid));
911         
912         /* updating answers saves events, don't save them with reply type */
913         if (tmp_method == ICAL_METHOD_REPLY)
914                 tmp_method = ICAL_METHOD_REQUEST;
915         
916         tmp = g_strdup_printf("%d", tmp_method);
917         xml_tag_add_attr(tag, xml_attr_new("method", tmp));
918         g_free(tmp);
919
920         tmp = g_strdup_printf("%d", event->sequence);
921         xml_tag_add_attr(tag, xml_attr_new("sequence", tmp));
922         g_free(tmp);
923         
924         tmp = g_strdup_printf("%d", event->type);
925         xml_tag_add_attr(tag, xml_attr_new("type", tmp));
926         g_free(tmp);
927         
928         tmp = g_strdup_printf("%lld", (long long)event->postponed);
929         xml_tag_add_attr(tag, xml_attr_new("postponed", tmp));
930         g_free(tmp);
931         
932         tmp = g_strdup_printf("%d", event->rec_occurrence);
933         xml_tag_add_attr(tag, xml_attr_new("rec_occurrence", tmp));
934         g_free(tmp);
935         
936         xmlnode = xml_node_new(tag, NULL);
937         rootnode = g_node_new(xmlnode);
938         
939         while (answers && answers->data) {
940                 XMLNode *ansxmlnode = NULL;
941                 GNode *ansnode = NULL;
942                 XMLTag *anstag = xml_tag_new("answer");
943                 Answer *a = (Answer *)answers->data;
944                 xml_tag_add_attr(anstag, xml_attr_new("attendee", a->attendee));
945                 xml_tag_add_attr(anstag, xml_attr_new("name", a->name?a->name:""));
946                 tmp = g_strdup_printf("%d", a->answer);
947                 xml_tag_add_attr(anstag, xml_attr_new("answer", tmp));
948                 g_free(tmp);
949                 tmp = g_strdup_printf("%d", a->cutype);
950                 xml_tag_add_attr(anstag, xml_attr_new("cutype", tmp));
951                 g_free(tmp);
952                 ansxmlnode = xml_node_new(anstag, NULL);
953                 ansnode = g_node_new(ansxmlnode);
954                 g_node_append(rootnode, ansnode);
955                 answers = answers->next;
956         }
957         
958         path = vcal_manager_get_event_file(event->uid);
959                                         
960         if ((pfile = prefs_write_open(path)) == NULL) {
961                 gchar *dir_path = vcal_manager_get_event_path();
962                 if (!is_dir_exist(dir_path) && make_dir(vcal_manager_get_event_path()) != 0) {
963                         g_free(dir_path);
964                         g_free(path);
965                         return;
966                 }
967                 g_free(dir_path);
968                 if ((pfile = prefs_write_open(path)) == NULL) {
969                         g_free(path);
970                         return;
971                 }
972         }
973         
974         g_free(path);
975         xml_file_put_xml_decl(pfile->fp);
976         xml_write_tree(rootnode, pfile->fp);
977         xml_free_tree(rootnode);
978
979         if (prefs_file_close(pfile) < 0) {
980                 g_warning("failed to write event.");
981                 return;
982         }
983  
984         if (export_after)
985                 vcal_folder_export(NULL);
986 }
987
988 static VCalEvent *event_get_from_xml (const gchar *uid, GNode *node)
989 {
990         XMLNode *xmlnode;
991         GList *list;
992         gchar *org = NULL, *location = NULL, *summary = NULL, *orgname = NULL;
993         gchar *dtstart = NULL, *dtend = NULL, *tzid = NULL;
994         gchar *description = NULL, *url = NULL, *recur = NULL;
995         VCalEvent *event = NULL;
996         icalproperty_method method = ICAL_METHOD_REQUEST;
997         icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
998         gint sequence = 0, rec_occurrence = 0;
999         time_t postponed = (time_t)0;
1000         
1001         g_return_val_if_fail(node->data != NULL, NULL);
1002
1003         xmlnode = node->data;
1004         if (strcmp2(xmlnode->tag->tag, "event") != 0) {
1005                 g_warning("tag name != \"event\"");
1006                 return NULL;
1007         }
1008         
1009         list = xmlnode->tag->attr;
1010         for (; list != NULL; list = list->next) {
1011                 XMLAttr *attr = list->data;
1012
1013                 if (!attr || !attr->name || !attr->value) continue;
1014                 if (!strcmp(attr->name, "organizer"))
1015                         org = g_strdup(attr->value);
1016                 if (!strcmp(attr->name, "orgname"))
1017                         orgname = g_strdup(attr->value);
1018                 if (!strcmp(attr->name, "location"))
1019                         location = g_strdup(attr->value);
1020                 if (!strcmp(attr->name, "summary"))
1021                         summary = g_strdup(attr->value);
1022                 if (!strcmp(attr->name, "description"))
1023                         description = g_strdup(attr->value);
1024                 if (!strcmp(attr->name, "url"))
1025                         url = g_strdup(attr->value);
1026                 if (!strcmp(attr->name, "dtstart"))
1027                         dtstart = g_strdup(attr->value);
1028                 if (!strcmp(attr->name, "dtend"))
1029                         dtend = g_strdup(attr->value);
1030                 if (!strcmp(attr->name, "recur"))
1031                         recur = g_strdup(attr->value);
1032                 if (!strcmp(attr->name, "tzid"))
1033                         tzid = g_strdup(attr->value);
1034                 if (!strcmp(attr->name, "type"))
1035                         type = atoi(attr->value);
1036                 if (!strcmp(attr->name, "method"))
1037                         method = atoi(attr->value);
1038                 if (!strcmp(attr->name, "sequence"))
1039                         sequence = atoi(attr->value);
1040                 if (!strcmp(attr->name, "postponed"))
1041                         postponed = atoi(attr->value);
1042                 if (!strcmp(attr->name, "rec_occurrence"))
1043                         rec_occurrence = atoi(attr->value);
1044         }
1045
1046         event = vcal_manager_new_event(uid, org, orgname, location, summary, description, 
1047                                         dtstart, dtend, recur, tzid, url, method, 
1048                                         sequence, type);
1049
1050         event->postponed = postponed;
1051         event->rec_occurrence = rec_occurrence;
1052
1053         g_free(org); 
1054         g_free(orgname); 
1055         g_free(location);
1056         g_free(summary); 
1057         g_free(description); 
1058         g_free(url); 
1059         g_free(dtstart); 
1060         g_free(dtend);
1061         g_free(recur);
1062         g_free(tzid);
1063
1064         node = node->children;
1065         while (node != NULL) {
1066                 gchar *attendee = NULL;
1067                 gchar *name = NULL;
1068                 icalparameter_partstat answer = ICAL_PARTSTAT_NEEDSACTION;
1069                 icalparameter_cutype cutype   = ICAL_CUTYPE_INDIVIDUAL;
1070                 
1071                 xmlnode = node->data;
1072                 if (strcmp2(xmlnode->tag->tag, "answer") != 0) {
1073                         g_warning("tag name != \"answer\"");
1074                         return event;
1075                 }
1076                 list = xmlnode->tag->attr;
1077                 for (; list != NULL; list = list->next) {
1078                         XMLAttr *attr = list->data;
1079
1080                         if (!attr || !attr->name || !attr->value) continue;
1081                         if (!strcmp(attr->name, "attendee"))
1082                                 attendee = g_strdup(attr->value);
1083                         if (!strcmp(attr->name, "name"))
1084                                 name = g_strdup(attr->value);
1085                         if (!strcmp(attr->name, "answer"))
1086                                 answer = atoi(attr->value);
1087                         if (!strcmp(attr->name, "cutype"))
1088                                 cutype = atoi(attr->value);
1089                 }
1090
1091                 event->answers = g_slist_prepend(event->answers, answer_new(attendee, name, answer, cutype));
1092                 g_free(attendee);
1093                 g_free(name);
1094                 node = node->next;
1095         }
1096         event->answers = g_slist_reverse(event->answers);
1097         
1098         return event;
1099 }
1100
1101 VCalEvent *vcal_manager_load_event (const gchar *uid)
1102 {
1103         GNode *node;
1104         gchar *path = NULL;
1105         VCalEvent *event = NULL;
1106
1107         path = vcal_manager_get_event_file(uid);
1108         
1109         if (!is_file_exist(path)) {
1110                 g_free(path);
1111                 return NULL;
1112         }
1113         
1114         node = xml_parse_file(path);
1115         
1116         g_free(path);
1117         
1118         if (!node) {
1119                 g_warning("no node");
1120                 return NULL;
1121         }
1122         
1123         event = event_get_from_xml(uid, node);
1124         /* vcal_manager_event_print(event); */
1125         xml_free_tree(node);
1126
1127         if (!event)
1128                 return NULL;
1129
1130         while (strchr(event->summary, '\n'))
1131                 *(strchr(event->summary, '\n')) = ' ';
1132
1133         return event;
1134         
1135 }
1136
1137 void vcal_manager_update_answer (VCalEvent      *event, 
1138                                  const gchar    *attendee,
1139                                  const gchar    *name,
1140                                  icalparameter_partstat ans,
1141                                  icalparameter_cutype cutype)
1142 {
1143         Answer *answer = NULL;
1144         GSList *existing = NULL;
1145         Answer *existing_a = NULL;
1146         
1147         if (!ans)
1148                 return;
1149                 
1150         answer = answer_new(attendee, name, ans, cutype);
1151         existing = answer_find(event, answer);
1152                 
1153         if (existing) {
1154                 existing_a = (Answer *)existing->data;
1155         
1156                 if (!answer->name && existing_a->name)
1157                         answer->name = g_strdup(existing_a->name);
1158                 if (!answer->cutype && existing_a->cutype)
1159                         answer->cutype = existing_a->cutype;
1160
1161                 answer_remove(event, answer);
1162         }
1163
1164         answer_add(event, answer);
1165         
1166         vcal_manager_save_event(event, FALSE);
1167 }
1168
1169 static gchar *write_headers(PrefsAccount        *account, 
1170                             VCalEvent           *event,
1171                             gboolean             short_headers,
1172                             gboolean             is_reply, 
1173                             gboolean             is_pseudo_display)
1174 {
1175         gchar *subject = NULL;
1176         gchar date[RFC822_DATE_BUFFSIZE];
1177         gchar *save_folder = NULL;
1178         gchar *result = NULL;
1179         gchar *queue_headers = NULL;
1180         gchar *method_str = NULL;
1181         gchar *attendees = NULL;
1182         icalparameter_partstat status;
1183         gchar *prefix = NULL;
1184         gchar enc_subject[512], enc_from[512], *from = NULL;
1185         gchar *msgid;
1186         gchar *calmsgid = NULL;
1187
1188         cm_return_val_if_fail(account != NULL, NULL);
1189
1190         memset(date, 0, sizeof(date));
1191         
1192         if (is_pseudo_display) {
1193                 struct icaltimetype itt = (icaltime_from_string(event->dtstart));
1194                 time_t t = icaltime_as_timet(itt);
1195                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1196         } else {
1197                 get_rfc822_date(date, sizeof(date));
1198         }
1199         
1200         if (account_get_special_folder(account, F_OUTBOX)) {
1201                 save_folder = folder_item_get_identifier(account_get_special_folder
1202                                   (account, F_OUTBOX));
1203         }
1204         
1205         if (!is_reply) {
1206                 GSList *cur = event->answers;
1207                 while (cur && cur->data) {
1208                         gchar *tmp = NULL;
1209                         Answer *a = (Answer *)cur->data;
1210                         
1211                         if (strcasecmp(a->attendee, event->organizer)) {
1212                                 if (attendees) {
1213                                         tmp = g_strdup_printf("%s>,\n <%s", attendees, a->attendee);
1214                                         g_free(attendees);
1215                                         attendees = tmp;
1216                                 } else {
1217                                         attendees = g_strdup_printf("%s", a->attendee);
1218                                 }
1219                         }
1220                         cur = cur->next;
1221                 }
1222         }
1223         
1224         if (!short_headers) {
1225                 queue_headers = g_strdup_printf("S:%s\n"
1226                                 "SSV:%s\n"
1227                                 "R:<%s>\n"
1228                                 "MAID:%d\n"
1229                                 "%s%s%s"
1230                                 "X-Claws-End-Special-Headers: 1\n",
1231                                 account->address,
1232                                 account->smtp_server,
1233                                 is_reply ? event->organizer:attendees,
1234                                 account->account_id,
1235                                 save_folder?"SCF:":"",
1236                                 save_folder?save_folder:"",
1237                                 save_folder?"\n":"");
1238         } else {
1239                 queue_headers = g_strdup("");
1240         }
1241         
1242         prefix = "";
1243         if (is_reply) {
1244                 method_str = "REPLY";
1245                 status = vcal_manager_get_reply_for_attendee(event, account->address);
1246                 if (status == ICAL_PARTSTAT_ACCEPTED)
1247                         prefix = _("Accepted: ");
1248                 else if (status == ICAL_PARTSTAT_DECLINED)
1249                         prefix = _("Declined: ");
1250                 else if (status == ICAL_PARTSTAT_TENTATIVE)
1251                         prefix = _("Tentatively Accepted: ");
1252                 else 
1253                         prefix = "Re: "; 
1254         } else if (event->method == ICAL_METHOD_PUBLISH) {
1255                 method_str = "PUBLISH";
1256         } else if (event->method == ICAL_METHOD_CANCEL) {
1257                 method_str = "CANCEL";
1258         } else {
1259                 method_str = "REQUEST";
1260         }
1261         
1262         subject = g_strdup_printf("%s%s", prefix, event->summary);
1263
1264         conv_encode_header_full(enc_subject, sizeof(enc_subject), subject, strlen("Subject: "), 
1265                         FALSE, conv_get_outgoing_charset_str());
1266         from = is_reply?account->name:(event->orgname?event->orgname:"");
1267         conv_encode_header_full(enc_from, sizeof(enc_from), from, strlen("From: "), 
1268                         TRUE, conv_get_outgoing_charset_str());
1269
1270         if (is_pseudo_display && event->uid) {
1271                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",event->uid);
1272         } else {
1273                 calmsgid = g_strdup("");
1274         }
1275
1276         msgid = prefs_account_generate_msgid(account);
1277
1278         result = g_strdup_printf("%s"
1279                                 "From: %s <%s>\n"
1280                                 "To: <%s>\n"
1281                                 "Subject: %s\n"
1282                                 "Date: %s\n"
1283                                 "MIME-Version: 1.0\n"
1284                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"\n"
1285                                 "Content-Transfer-Encoding: 8bit\n"
1286                                 "%s"
1287                                 "%s: <%s>\n",
1288                                 queue_headers,
1289                                 enc_from,
1290                                 is_reply ? account->address:event->organizer,
1291                                 is_reply ? event->organizer:(attendees?attendees:event->organizer),
1292                                 enc_subject,
1293                                 date,
1294                                 method_str,
1295                                 CS_UTF_8,
1296                                 calmsgid,
1297                                 is_pseudo_display?
1298                                         "In-Reply-To":"Message-ID",
1299                                 is_pseudo_display?
1300                                         event_to_today_str(event, 0):msgid);
1301         g_free(calmsgid);
1302         g_free(subject);
1303         g_free(save_folder);
1304         g_free(queue_headers);
1305         g_free(attendees);
1306         g_free(msgid);
1307         return result;                  
1308                                                                                
1309
1310 }
1311
1312 static gchar *write_headers_ical(PrefsAccount   *account, 
1313                             icalcomponent       *ievent,
1314                             gchar               *orga)
1315 {
1316         gchar subject[512];
1317         gchar date[RFC822_DATE_BUFFSIZE];
1318         gchar *result = NULL;
1319         gchar *method_str = NULL;
1320         gchar *summary = NULL;
1321         gchar *organizer = NULL;
1322         gchar *orgname = NULL;
1323         icalproperty *prop = NULL;
1324         gchar *calmsgid = NULL;
1325
1326         time_t t = (time_t)0;
1327
1328         memset(subject, 0, sizeof(subject));
1329         memset(date, 0, sizeof(date));
1330         
1331         prop = icalcomponent_get_first_property(ievent, ICAL_SUMMARY_PROPERTY);
1332         if (prop) {
1333                 summary = g_strdup(icalproperty_get_summary(prop));
1334                 icalproperty_free(prop);
1335         } else {
1336                 summary = g_strdup("");
1337         }
1338         
1339         while (strchr(summary, '\n'))
1340                 *(strchr(summary, '\n')) = ' ';
1341
1342         prop = icalcomponent_get_first_property(ievent, ICAL_ORGANIZER_PROPERTY);
1343         if (prop) {
1344                 organizer = g_strdup(icalproperty_get_organizer(prop));
1345                 if (icalproperty_get_parameter_as_string(prop, "CN") != NULL)
1346                         orgname = g_strdup(icalproperty_get_parameter_as_string(prop, "CN"));
1347
1348                 icalproperty_free(prop);
1349         } else {
1350                 organizer = orga? g_strdup(orga):g_strdup("");
1351         }
1352
1353         prop = icalcomponent_get_first_property(ievent, ICAL_DTSTART_PROPERTY);
1354         if (prop) {
1355                 t = icaltime_as_timet(icalproperty_get_dtstart(prop));
1356                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1357         } else {
1358                 get_rfc822_date(date, sizeof(date));
1359         }
1360
1361         conv_encode_header(subject, 511, summary, strlen("Subject: "), FALSE);
1362                         
1363         method_str = "PUBLISH";
1364
1365         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
1366         if (prop) {
1367                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop));
1368                 icalproperty_free(prop);
1369         } else {
1370                 calmsgid = g_strdup("");
1371         }
1372         
1373
1374         result = g_strdup_printf("From: %s <%s>\n"
1375                                 "To: <%s>\n"
1376                                 "Subject: %s%s\n"
1377                                 "Date: %s\n"
1378                                 "MIME-Version: 1.0\n"
1379                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n"
1380                                 "Content-Transfer-Encoding: quoted-printable\n"
1381                                 "%s"
1382                                 "In-Reply-To: <%s>\n",
1383                                 orgname?orgname:"",
1384                                 !strncmp(organizer, "MAILTO:", 7) ? organizer+7 : organizer,
1385                                 account->address,
1386                                 "",
1387                                 subject,
1388                                 date,
1389                                 method_str,
1390                                 conv_get_outgoing_charset_str(),
1391                                 calmsgid,
1392                                 event_to_today_str(NULL, t));
1393         
1394         g_free(calmsgid);
1395         g_free(orgname);
1396         g_free(organizer);
1397         g_free(summary);
1398
1399         return result;                  
1400                                                                                
1401
1402 }
1403
1404 static gboolean vcal_manager_send (PrefsAccount         *account, 
1405                                      VCalEvent          *event,
1406                                      gboolean            is_reply)
1407 {
1408         gchar *tmpfile = NULL;
1409         gint msgnum;
1410         FolderItem *folderitem;
1411         gchar *msgpath = NULL;
1412         Folder *folder = NULL;
1413         
1414         tmpfile = vcal_manager_event_dump(event, is_reply, FALSE, NULL, TRUE);
1415
1416         if (!tmpfile)
1417                 return FALSE;
1418
1419         folderitem = account_get_special_folder(account, F_QUEUE);
1420         if (!folderitem) {
1421                 g_warning("can't find queue folder for %s", account->address);
1422                 g_unlink(tmpfile);
1423                 g_free(tmpfile);
1424                 return FALSE;
1425         }
1426         folder_item_scan(folderitem);
1427         
1428         if ((msgnum = folder_item_add_msg(folderitem, tmpfile, NULL, TRUE)) < 0) {
1429                 g_warning("can't queue the message");
1430                 g_unlink(tmpfile);
1431                 g_free(tmpfile);
1432                 return FALSE;
1433         }
1434
1435         msgpath = folder_item_fetch_msg(folderitem, msgnum);
1436         
1437         if (!prefs_common_get_prefs()->work_offline) {
1438                 gchar *err = NULL;
1439                 gboolean queued_removed = FALSE;
1440                 gint val = procmsg_send_message_queue_with_lock(msgpath, &err, folderitem, msgnum, &queued_removed);
1441                 if (val == 0) {
1442                         if (!queued_removed)
1443                                 folder_item_remove_msg(folderitem, msgnum);
1444                         folder_item_scan(folderitem);
1445                 } else if (err) {
1446                         alertpanel_error_log("%s", err);
1447                         g_free(err);
1448                 }
1449         }
1450         g_unlink(tmpfile);
1451         g_free(tmpfile);
1452         g_free(msgpath);
1453
1454         folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1455         if (folder) {
1456                 folder_item_scan(folder->inbox);
1457                 vcalviewer_reload(folder->inbox);
1458         } else
1459                 g_warning("couldn't find vCalendar folder class");
1460         return TRUE;
1461 }
1462
1463 gboolean vcal_manager_reply (PrefsAccount       *account, 
1464                              VCalEvent          *event)
1465 {
1466         return vcal_manager_send(account, event, TRUE);
1467 }
1468
1469 gboolean vcal_manager_request (PrefsAccount     *account, 
1470                                VCalEvent        *event)
1471 {
1472         return vcal_manager_send(account, event, FALSE);
1473 }
1474
1475 EventTime event_to_today(VCalEvent *event, time_t t)
1476 {
1477         struct tm evtstart, today;
1478         time_t evtstart_t, today_t;
1479         struct icaltimetype itt;
1480
1481         tzset();
1482         
1483         today_t = time(NULL);
1484         if (event) {
1485                 itt = icaltime_from_string(event->dtstart);
1486                 evtstart_t = icaltime_as_timet(itt);
1487         } else {
1488                 evtstart_t = t;
1489         }
1490         
1491 #ifndef G_OS_WIN32
1492         struct tm buft;
1493         today = *localtime_r(&today_t, &buft);
1494         localtime_r(&evtstart_t, &evtstart);
1495 #else
1496         if (today_t < 0)
1497                 today_t = 1;
1498         if (evtstart_t < 0)
1499                 evtstart_t = 1;
1500         today = *localtime(&today_t);
1501         evtstart = *localtime(&evtstart_t);
1502 #endif
1503         
1504         if (today.tm_year == evtstart.tm_year) {
1505                 int days = evtstart.tm_yday - today.tm_yday;
1506                 if (days < 0) {
1507                         return EVENT_PAST;
1508                 } else if (days == 0) {
1509                         return EVENT_TODAY;
1510                 } else if (days == 1) {
1511                         return EVENT_TOMORROW;
1512                 } else if (days > 1 && days < 7) {
1513                         return EVENT_THISWEEK;
1514                 } else {
1515                         return EVENT_LATER;
1516                 }
1517         } else if (today.tm_year > evtstart.tm_year) {
1518                 return EVENT_PAST;
1519         } else if (today.tm_year == evtstart.tm_year - 1) {
1520                 int days = ((365 - today.tm_yday) + evtstart.tm_yday);
1521                 if (days == 0) {
1522                         return EVENT_TODAY;
1523                 } else if (days == 1) {
1524                         return EVENT_TOMORROW;
1525                 } else if (days > 1 && days < 7) {
1526                         return EVENT_THISWEEK;
1527                 } else {
1528                         return EVENT_LATER;
1529                 }
1530         } else 
1531                 return EVENT_LATER;
1532 }
1533
1534 const gchar *event_to_today_str(VCalEvent *event, time_t t)
1535 {
1536         EventTime days = event_to_today(event, t);
1537         switch(days) {
1538         case EVENT_PAST:
1539                 return EVENT_PAST_ID;
1540         case EVENT_TODAY:
1541                 return EVENT_TODAY_ID;
1542         case EVENT_TOMORROW:
1543                 return EVENT_TOMORROW_ID;
1544         case EVENT_THISWEEK:
1545                 return EVENT_THISWEEK_ID;
1546         case EVENT_LATER:
1547                 return EVENT_LATER_ID;
1548         }
1549         return NULL;
1550 }