vCalendar plugin: use external libical
[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-2007 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(time(NULL), TRUE), (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(time(NULL), TRUE), (void*)0));
427         icalcomponent_add_property(ievent,
428                 icalproperty_vanew_lastmodified(icaltime_from_timet(time(NULL), TRUE), (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         sscanf(asctime_r(lt, buft1), "%3s %3s %d %d:%d:%d %d\n",
533                day, mon, &dd, &hh, &mm, &ss, &yyyy);
534         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
535                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
536 #else
537         GDateTime *dt = g_date_time_new_from_unix_local(t);
538         gchar *buf2 = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
539         g_date_time_unref(dt);
540         strncpy(buf, buf2, len);
541         g_free(buf2);
542 #endif
543 }
544
545 static gchar *write_headers_date(const gchar *uid)
546 {
547         gchar subject[512];
548         gchar *t_subject;
549         gchar date[RFC822_DATE_BUFFSIZE];
550         time_t t;
551         struct tm lt;
552
553         memset(subject, 0, sizeof(subject));
554         memset(date, 0, sizeof(date));
555         
556         if (!strcmp(uid, EVENT_PAST_ID)) {
557                 t = 1;
558                 t_subject = _("Past");
559         } else if (!strcmp(uid, EVENT_TODAY_ID)) {
560                 t = time(NULL);
561                 t_subject = _("Today");
562         }  else if (!strcmp(uid, EVENT_TOMORROW_ID)) {
563                 t = time(NULL) + 86400;
564                 t_subject = _("Tomorrow");
565         }  else if (!strcmp(uid, EVENT_THISWEEK_ID)) {
566                 t = time(NULL) + (86400*2);
567                 t_subject = _("This week");
568         }  else if (!strcmp(uid, EVENT_LATER_ID)) {
569                 t = time(NULL) + (86400*7);
570                 t_subject = _("Later");
571         }  else {
572                 g_warning("unknown spec date");
573                 return NULL;
574         } 
575         
576 #ifndef G_OS_WIN32
577         struct tm buft;
578         lt = *localtime_r(&t, &buft);
579 #else
580         if (t < 0)
581                 t = 1;
582         lt = *localtime(&t);
583 #endif
584         lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
585         t = mktime(&lt);
586         get_rfc822_date_from_time_t(date, sizeof(date), t);
587         conv_encode_header(subject, 511, t_subject, strlen("Subject: "), FALSE);
588                                 
589         return g_strdup_printf("From: -\n"
590                                 "To: -\n"
591                                 "Subject: %s\n"
592                                 "Date: %s\n"
593                                 "MIME-Version: 1.0\n"
594                                 "Content-Type: text/plain; charset=\"UTF-8\";\n"
595                                 "Content-Transfer-Encoding: quoted-printable\n"
596                                 "Message-ID: <%s>\n",
597                                 subject,
598                                 date,
599                                 uid);
600 }
601
602 gchar *vcal_manager_dateevent_dump(const gchar *uid, FolderItem *item)
603 {
604         gchar *sanitized_uid = NULL;
605         gchar *headers = NULL;
606         gchar *lines, *body, *tmpfile;
607         EventTime date;
608         sanitized_uid = g_strdup(uid);
609         subst_for_filename(sanitized_uid);
610         
611         tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
612                                    G_DIR_SEPARATOR, getuid(), sanitized_uid);
613         g_free(sanitized_uid);
614
615         headers = write_headers_date(uid);
616
617         if (!headers) {
618                 g_warning("can't get headers");
619                 g_free(tmpfile);
620                 return NULL;
621         }
622
623         if (!strcmp(uid, EVENT_PAST_ID))
624                 date = EVENT_PAST;
625         else if (!strcmp(uid, EVENT_TODAY_ID))
626                 date = EVENT_TODAY;
627         else if (!strcmp(uid, EVENT_TOMORROW_ID))
628                 date = EVENT_TOMORROW;
629         else if (!strcmp(uid, EVENT_THISWEEK_ID))
630                 date = EVENT_THISWEEK;
631         else if (!strcmp(uid, EVENT_LATER_ID))
632                 date = EVENT_LATER;
633         else
634                 date = EVENT_PAST;
635
636         lines = get_item_event_list_for_date(item, date);
637         body = g_strdup_printf("%s"
638                                "\n"
639                                "%s", headers, lines);
640         g_free(lines);
641         if (str_write_to_file(body, tmpfile) < 0) {
642                 g_free(tmpfile);
643                 tmpfile = NULL;
644         } else
645                 chmod(tmpfile, S_IRUSR|S_IWUSR);
646
647         g_free(body);
648         g_free(headers);
649         
650         return tmpfile; 
651 }
652
653 static gchar *write_headers_ical(PrefsAccount   *account, 
654                             icalcomponent       *ievent,
655                             gchar               *orga);
656
657 gchar *vcal_manager_icalevent_dump(icalcomponent *event, gchar *orga, icalcomponent *use_calendar)
658 {
659         PrefsAccount *account = account_get_cur_account();
660         gchar *body, *headers, *qpbody;
661         gchar **lines = NULL;
662         gchar *tmpfile = NULL;
663         icalcomponent *calendar;
664         icalproperty *prop;
665         icalcomponent *ievent = NULL;
666         int i = 0;
667
668         ievent = icalcomponent_new_clone(event);
669
670         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
671         if (prop) {
672                 gchar *sanitized_uid = g_strdup(icalproperty_get_uid(prop));
673
674                 subst_for_filename(sanitized_uid);
675
676                 tmpfile = g_strdup_printf("%s%cevt-%d-%s", get_tmp_dir(),
677                                               G_DIR_SEPARATOR, getuid(), sanitized_uid);
678                 g_free(sanitized_uid);
679                 icalproperty_free(prop);
680         } else {
681                 tmpfile = g_strdup_printf("%s%cevt-%d-%p", get_tmp_dir(),
682                                       G_DIR_SEPARATOR, getuid(), ievent);
683         }
684
685         if (!account) {
686                 g_free(tmpfile);
687                 icalcomponent_free(ievent);
688                 return NULL;
689         }
690
691         tzset();
692         
693         if (use_calendar != NULL) {
694                 calendar = use_calendar;
695                 g_free(tmpfile);
696                 tmpfile = NULL;
697         } else
698                 calendar = 
699                         icalcomponent_vanew(
700                             ICAL_VCALENDAR_COMPONENT,
701                             icalproperty_new_version("2.0"),
702                             icalproperty_new_prodid(
703                                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
704                             icalproperty_new_calscale("GREGORIAN"),
705                             icalproperty_new_method(ICAL_METHOD_PUBLISH),
706                             (void*)0
707                             );  
708
709         if (!calendar) {
710                 g_warning ("can't generate calendar");
711                 g_free(tmpfile);
712                 icalcomponent_free(ievent);
713                 return NULL;
714         }
715         
716         icalcomponent_add_component(calendar, ievent);
717
718         if (use_calendar)
719                 return NULL;
720
721         headers = write_headers_ical(account, ievent, orga);
722
723         if (!headers) {
724                 g_warning("can't get headers");
725                 g_free(tmpfile);
726                 icalcomponent_free(calendar);
727                 return NULL;
728         }
729
730         lines = g_strsplit(icalcomponent_as_ical_string(calendar), "\n", 0);
731         qpbody = g_strdup("");
732         
733         /* encode to quoted-printable */
734         while (lines[i]) {
735                 gint e_len = strlen(qpbody), n_len = 0;
736                 gchar *outline = conv_codeset_strdup(lines[i], CS_UTF_8, conv_get_outgoing_charset_str());
737                 gchar *qpoutline = g_malloc(strlen(outline)*8 + 1);
738                 
739                 qp_encode_line(qpoutline, (guchar *)outline);
740                 n_len = strlen(qpoutline);
741                 
742                 qpbody = g_realloc(qpbody, e_len + n_len + 1);
743                 strcpy(qpbody+e_len, qpoutline);
744                 *(qpbody+n_len+e_len) = '\0';
745                 
746                 g_free(outline);
747                 g_free(qpoutline);
748                 i++;
749         }
750         
751         body = g_strdup_printf("%s"
752                                "\n"
753                                "%s", headers, qpbody);
754         
755         if (str_write_to_file(body, tmpfile) < 0) {
756                 g_free(tmpfile);
757                 tmpfile = NULL;
758         } else
759                 chmod(tmpfile, S_IRUSR|S_IWUSR);
760
761         g_strfreev(lines);
762         g_free(body);
763         g_free(qpbody);
764         g_free(headers);
765         icalcomponent_free(calendar);
766         
767         return tmpfile; 
768 }
769
770 VCalEvent * vcal_manager_new_event      (const gchar    *uid, 
771                                          const gchar    *organizer,
772                                          const gchar    *orgname,
773                                          const gchar    *location,
774                                          const gchar    *summary,
775                                          const gchar    *description,
776                                          const gchar    *dtstart,
777                                          const gchar    *dtend,
778                                          const gchar    *recur,
779                                          const gchar    *tzid,
780                                          const gchar    *url,
781                                          icalproperty_method method,
782                                          gint            sequence,
783                                          icalcomponent_kind type)
784 {
785         VCalEvent *event = g_new0(VCalEvent, 1);
786
787         event->uid              = g_strdup(uid?uid:"");
788         event->organizer        = g_strdup(organizer?organizer:"");
789         event->orgname          = g_strdup(orgname?orgname:"");
790
791         if (dtend && *(dtend)) {
792                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtend)));
793 #ifdef G_OS_WIN32
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 #else
798                 gchar buft[512];
799                 tzset();
800                 event->end      = g_strdup(ctime_r(&tmp, buft));
801 #endif
802         }
803         
804         if (dtstart && *(dtstart)) {
805                 time_t tmp = icaltime_as_timet((icaltime_from_string(dtstart)));
806 #ifdef G_OS_WIN32
807                 GDateTime *dt = g_date_time_new_from_unix_local(tmp);
808                 event->start = g_date_time_format(dt, "%a, %e %b %Y %H:%M:%S %z");
809                 g_date_time_unref(dt);
810 #else
811                 gchar buft[512];
812                 tzset();
813                 event->start    = g_strdup(ctime_r(&tmp, buft));
814 #endif
815         }
816         event->dtstart          = g_strdup(dtstart?dtstart:"");
817         event->dtend            = g_strdup(dtend?dtend:"");
818         event->recur            = g_strdup(recur?recur:"");
819         event->location         = g_strdup(location?location:"");
820         event->summary          = g_strdup(summary?summary:"");
821         event->description      = g_strdup(description?description:"");
822         event->url              = g_strdup(url?url:"");
823         event->tzid             = g_strdup(tzid?tzid:"");
824         event->method           = method;
825         event->sequence         = sequence;
826         event->type             = type;
827         event->rec_occurrence           = FALSE;
828         while (strchr(event->summary, '\n'))
829                 *(strchr(event->summary, '\n')) = ' ';
830
831         return event;
832 }
833                                          
834 void vcal_manager_free_event (VCalEvent *event)
835 {
836         GSList *cur;
837         if (!event)
838                 return;
839
840         g_free(event->uid);
841         g_free(event->organizer);
842         g_free(event->orgname);
843         g_free(event->start);
844         g_free(event->end);
845         g_free(event->location);
846         g_free(event->summary);
847         g_free(event->dtstart);
848         g_free(event->dtend);
849         g_free(event->recur);
850         g_free(event->tzid);
851         g_free(event->description);
852         g_free(event->url);
853         for (cur = event->answers; cur; cur = cur->next) {
854                 answer_free((Answer *)cur->data);
855         }
856         g_slist_free(event->answers);
857         g_free(event);
858 }
859
860 gchar *vcal_manager_get_event_path(void)
861 {
862         static gchar *event_path = NULL;
863         if (!event_path)
864                 event_path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
865                                         "vcalendar", NULL);
866         
867         return event_path;
868 }
869
870 gchar *vcal_manager_get_event_file(const gchar *uid)
871 {
872         gchar *tmp = g_strdup(uid);
873         gchar *res = NULL;
874
875         subst_for_filename(tmp);
876         res = g_strconcat(vcal_manager_get_event_path(), G_DIR_SEPARATOR_S,
877                            tmp, NULL);
878         g_free(tmp);
879         return res;
880 }
881
882 PrefsAccount *vcal_manager_get_account_from_event(VCalEvent *event)
883 {
884         GSList *list = vcal_manager_get_answers_emails(event);
885         GSList *cur = list;
886         
887         /* find an attendee corresponding to one of our accounts */
888         while (cur && cur->data) {
889                 gchar *email = (gchar *)cur->data;
890                 if (account_find_from_address(email, FALSE)) {
891                         g_slist_free(list);
892                         return account_find_from_address(email, FALSE);
893                 }
894                 cur = cur->next;
895         }
896         g_slist_free(list);
897         return NULL;
898 }
899
900 void vcal_manager_save_event (VCalEvent *event, gboolean export_after)
901 {
902         XMLTag *tag = NULL;
903         XMLNode *xmlnode = NULL;
904         GNode *rootnode = NULL;
905         PrefFile *pfile;
906         gchar *path = NULL;
907         GSList *answers = event->answers;
908         gchar *tmp = NULL;
909         gint tmp_method = event->method;
910         
911         tag = xml_tag_new("event");
912         xml_tag_add_attr(tag, xml_attr_new("organizer", event->organizer));
913         xml_tag_add_attr(tag, xml_attr_new("orgname", event->orgname));
914         xml_tag_add_attr(tag, xml_attr_new("location", event->location));
915         xml_tag_add_attr(tag, xml_attr_new("summary", event->summary));
916         xml_tag_add_attr(tag, xml_attr_new("description", event->description));
917         xml_tag_add_attr(tag, xml_attr_new("url", event->url));
918         xml_tag_add_attr(tag, xml_attr_new("dtstart", event->dtstart));
919         xml_tag_add_attr(tag, xml_attr_new("dtend", event->dtend));
920         xml_tag_add_attr(tag, xml_attr_new("recur", event->recur));
921         xml_tag_add_attr(tag, xml_attr_new("tzid", event->tzid));
922         
923         /* updating answers saves events, don't save them with reply type */
924         if (tmp_method == ICAL_METHOD_REPLY)
925                 tmp_method = ICAL_METHOD_REQUEST;
926         
927         tmp = g_strdup_printf("%d", tmp_method);
928         xml_tag_add_attr(tag, xml_attr_new("method", tmp));
929         g_free(tmp);
930
931         tmp = g_strdup_printf("%d", event->sequence);
932         xml_tag_add_attr(tag, xml_attr_new("sequence", tmp));
933         g_free(tmp);
934         
935         tmp = g_strdup_printf("%d", event->type);
936         xml_tag_add_attr(tag, xml_attr_new("type", tmp));
937         g_free(tmp);
938         
939         tmp = g_strdup_printf("%lld", (long long)event->postponed);
940         xml_tag_add_attr(tag, xml_attr_new("postponed", tmp));
941         g_free(tmp);
942         
943         tmp = g_strdup_printf("%d", event->rec_occurrence);
944         xml_tag_add_attr(tag, xml_attr_new("rec_occurrence", tmp));
945         g_free(tmp);
946         
947         xmlnode = xml_node_new(tag, NULL);
948         rootnode = g_node_new(xmlnode);
949         
950         while (answers && answers->data) {
951                 XMLNode *ansxmlnode = NULL;
952                 GNode *ansnode = NULL;
953                 XMLTag *anstag = xml_tag_new("answer");
954                 Answer *a = (Answer *)answers->data;
955                 xml_tag_add_attr(anstag, xml_attr_new("attendee", a->attendee));
956                 xml_tag_add_attr(anstag, xml_attr_new("name", a->name?a->name:""));
957                 tmp = g_strdup_printf("%d", a->answer);
958                 xml_tag_add_attr(anstag, xml_attr_new("answer", tmp));
959                 g_free(tmp);
960                 tmp = g_strdup_printf("%d", a->cutype);
961                 xml_tag_add_attr(anstag, xml_attr_new("cutype", tmp));
962                 g_free(tmp);
963                 ansxmlnode = xml_node_new(anstag, NULL);
964                 ansnode = g_node_new(ansxmlnode);
965                 g_node_append(rootnode, ansnode);
966                 answers = answers->next;
967         }
968         
969         path = vcal_manager_get_event_file(event->uid);
970                                         
971         if ((pfile = prefs_write_open(path)) == NULL) {
972                 gchar *dir_path = vcal_manager_get_event_path();
973                 if (!is_dir_exist(dir_path) && make_dir(vcal_manager_get_event_path()) != 0) {
974                         g_free(dir_path);
975                         g_free(path);
976                         return;
977                 }
978                 g_free(dir_path);
979                 if ((pfile = prefs_write_open(path)) == NULL) {
980                         g_free(path);
981                         return;
982                 }
983         }
984         
985         g_free(path);
986         xml_file_put_xml_decl(pfile->fp);
987         xml_write_tree(rootnode, pfile->fp);
988         xml_free_tree(rootnode);
989
990         if (prefs_file_close(pfile) < 0) {
991                 g_warning("failed to write event.");
992                 return;
993         }
994  
995         if (export_after)
996                 vcal_folder_export(NULL);
997 }
998
999 static VCalEvent *event_get_from_xml (const gchar *uid, GNode *node)
1000 {
1001         XMLNode *xmlnode;
1002         GList *list;
1003         gchar *org = NULL, *location = NULL, *summary = NULL, *orgname = NULL;
1004         gchar *dtstart = NULL, *dtend = NULL, *tzid = NULL;
1005         gchar *description = NULL, *url = NULL, *recur = NULL;
1006         VCalEvent *event = NULL;
1007         icalproperty_method method = ICAL_METHOD_REQUEST;
1008         icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
1009         gint sequence = 0, rec_occurrence = 0;
1010         time_t postponed = (time_t)0;
1011         
1012         g_return_val_if_fail(node->data != NULL, NULL);
1013
1014         xmlnode = node->data;
1015         if (strcmp2(xmlnode->tag->tag, "event") != 0) {
1016                 g_warning("tag name != \"event\"");
1017                 return NULL;
1018         }
1019         
1020         list = xmlnode->tag->attr;
1021         for (; list != NULL; list = list->next) {
1022                 XMLAttr *attr = list->data;
1023
1024                 if (!attr || !attr->name || !attr->value) continue;
1025                 if (!strcmp(attr->name, "organizer"))
1026                         org = g_strdup(attr->value);
1027                 if (!strcmp(attr->name, "orgname"))
1028                         orgname = g_strdup(attr->value);
1029                 if (!strcmp(attr->name, "location"))
1030                         location = g_strdup(attr->value);
1031                 if (!strcmp(attr->name, "summary"))
1032                         summary = g_strdup(attr->value);
1033                 if (!strcmp(attr->name, "description"))
1034                         description = g_strdup(attr->value);
1035                 if (!strcmp(attr->name, "url"))
1036                         url = g_strdup(attr->value);
1037                 if (!strcmp(attr->name, "dtstart"))
1038                         dtstart = g_strdup(attr->value);
1039                 if (!strcmp(attr->name, "dtend"))
1040                         dtend = g_strdup(attr->value);
1041                 if (!strcmp(attr->name, "recur"))
1042                         recur = g_strdup(attr->value);
1043                 if (!strcmp(attr->name, "tzid"))
1044                         tzid = g_strdup(attr->value);
1045                 if (!strcmp(attr->name, "type"))
1046                         type = atoi(attr->value);
1047                 if (!strcmp(attr->name, "method"))
1048                         method = atoi(attr->value);
1049                 if (!strcmp(attr->name, "sequence"))
1050                         sequence = atoi(attr->value);
1051                 if (!strcmp(attr->name, "postponed"))
1052                         postponed = atoi(attr->value);
1053                 if (!strcmp(attr->name, "rec_occurrence"))
1054                         rec_occurrence = atoi(attr->value);
1055         }
1056
1057         event = vcal_manager_new_event(uid, org, orgname, location, summary, description, 
1058                                         dtstart, dtend, recur, tzid, url, method, 
1059                                         sequence, type);
1060
1061         event->postponed = postponed;
1062         event->rec_occurrence = rec_occurrence;
1063
1064         g_free(org); 
1065         g_free(orgname); 
1066         g_free(location);
1067         g_free(summary); 
1068         g_free(description); 
1069         g_free(url); 
1070         g_free(dtstart); 
1071         g_free(dtend);
1072         g_free(recur);
1073         g_free(tzid);
1074
1075         node = node->children;
1076         while (node != NULL) {
1077                 gchar *attendee = NULL;
1078                 gchar *name = NULL;
1079                 icalparameter_partstat answer = ICAL_PARTSTAT_NEEDSACTION;
1080                 icalparameter_cutype cutype   = ICAL_CUTYPE_INDIVIDUAL;
1081                 
1082                 xmlnode = node->data;
1083                 if (strcmp2(xmlnode->tag->tag, "answer") != 0) {
1084                         g_warning("tag name != \"answer\"");
1085                         return event;
1086                 }
1087                 list = xmlnode->tag->attr;
1088                 for (; list != NULL; list = list->next) {
1089                         XMLAttr *attr = list->data;
1090
1091                         if (!attr || !attr->name || !attr->value) continue;
1092                         if (!strcmp(attr->name, "attendee"))
1093                                 attendee = g_strdup(attr->value);
1094                         if (!strcmp(attr->name, "name"))
1095                                 name = g_strdup(attr->value);
1096                         if (!strcmp(attr->name, "answer"))
1097                                 answer = atoi(attr->value);
1098                         if (!strcmp(attr->name, "cutype"))
1099                                 cutype = atoi(attr->value);
1100                 }
1101
1102                 event->answers = g_slist_prepend(event->answers, answer_new(attendee, name, answer, cutype));
1103                 g_free(attendee);
1104                 g_free(name);
1105                 node = node->next;
1106         }
1107         event->answers = g_slist_reverse(event->answers);
1108         
1109         return event;
1110 }
1111
1112 VCalEvent *vcal_manager_load_event (const gchar *uid)
1113 {
1114         GNode *node;
1115         gchar *path = NULL;
1116         VCalEvent *event = NULL;
1117
1118         path = vcal_manager_get_event_file(uid);
1119         
1120         if (!is_file_exist(path)) {
1121                 g_free(path);
1122                 return NULL;
1123         }
1124         
1125         node = xml_parse_file(path);
1126         
1127         g_free(path);
1128         
1129         if (!node) {
1130                 g_warning("no node");
1131                 return NULL;
1132         }
1133         
1134         event = event_get_from_xml(uid, node);
1135         /* vcal_manager_event_print(event); */
1136         xml_free_tree(node);
1137
1138         if (!event)
1139                 return NULL;
1140
1141         while (strchr(event->summary, '\n'))
1142                 *(strchr(event->summary, '\n')) = ' ';
1143
1144         return event;
1145         
1146 }
1147
1148 void vcal_manager_update_answer (VCalEvent      *event, 
1149                                  const gchar    *attendee,
1150                                  const gchar    *name,
1151                                  icalparameter_partstat ans,
1152                                  icalparameter_cutype cutype)
1153 {
1154         Answer *answer = NULL;
1155         GSList *existing = NULL;
1156         Answer *existing_a = NULL;
1157         
1158         if (!ans)
1159                 return;
1160                 
1161         answer = answer_new(attendee, name, ans, cutype);
1162         existing = answer_find(event, answer);
1163                 
1164         if (existing) {
1165                 existing_a = (Answer *)existing->data;
1166         
1167                 if (!answer->name && existing_a->name)
1168                         answer->name = g_strdup(existing_a->name);
1169                 if (!answer->cutype && existing_a->cutype)
1170                         answer->cutype = existing_a->cutype;
1171
1172                 answer_remove(event, answer);
1173         }
1174
1175         answer_add(event, answer);
1176         
1177         vcal_manager_save_event(event, FALSE);
1178 }
1179
1180 static gchar *write_headers(PrefsAccount        *account, 
1181                             VCalEvent           *event,
1182                             gboolean             short_headers,
1183                             gboolean             is_reply, 
1184                             gboolean             is_pseudo_display)
1185 {
1186         gchar *subject = NULL;
1187         gchar date[RFC822_DATE_BUFFSIZE];
1188         gchar *save_folder = NULL;
1189         gchar *result = NULL;
1190         gchar *queue_headers = NULL;
1191         gchar *method_str = NULL;
1192         gchar *attendees = NULL;
1193         icalparameter_partstat status;
1194         gchar *prefix = NULL;
1195         gchar enc_subject[512], enc_from[512], *from = NULL;
1196         gchar *msgid;
1197         gchar *calmsgid = NULL;
1198
1199         cm_return_val_if_fail(account != NULL, NULL);
1200
1201         memset(date, 0, sizeof(date));
1202         
1203         if (is_pseudo_display) {
1204                 struct icaltimetype itt = (icaltime_from_string(event->dtstart));
1205                 time_t t = icaltime_as_timet(itt);
1206                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1207         } else {
1208                 get_rfc822_date(date, sizeof(date));
1209         }
1210         
1211         if (account_get_special_folder(account, F_OUTBOX)) {
1212                 save_folder = folder_item_get_identifier(account_get_special_folder
1213                                   (account, F_OUTBOX));
1214         }
1215         
1216         if (!is_reply) {
1217                 GSList *cur = event->answers;
1218                 while (cur && cur->data) {
1219                         gchar *tmp = NULL;
1220                         Answer *a = (Answer *)cur->data;
1221                         
1222                         if (strcasecmp(a->attendee, event->organizer)) {
1223                                 if (attendees) {
1224                                         tmp = g_strdup_printf("%s>,\n <%s", attendees, a->attendee);
1225                                         g_free(attendees);
1226                                         attendees = tmp;
1227                                 } else {
1228                                         attendees = g_strdup_printf("%s", a->attendee);
1229                                 }
1230                         }
1231                         cur = cur->next;
1232                 }
1233         }
1234         
1235         if (!short_headers) {
1236                 queue_headers = g_strdup_printf("S:%s\n"
1237                                 "SSV:%s\n"
1238                                 "R:<%s>\n"
1239                                 "MAID:%d\n"
1240                                 "%s%s%s"
1241                                 "X-Claws-End-Special-Headers: 1\n",
1242                                 account->address,
1243                                 account->smtp_server,
1244                                 is_reply ? event->organizer:attendees,
1245                                 account->account_id,
1246                                 save_folder?"SCF:":"",
1247                                 save_folder?save_folder:"",
1248                                 save_folder?"\n":"");
1249         } else {
1250                 queue_headers = g_strdup("");
1251         }
1252         
1253         prefix = "";
1254         if (is_reply) {
1255                 method_str = "REPLY";
1256                 status = vcal_manager_get_reply_for_attendee(event, account->address);
1257                 if (status == ICAL_PARTSTAT_ACCEPTED)
1258                         prefix = _("Accepted: ");
1259                 else if (status == ICAL_PARTSTAT_DECLINED)
1260                         prefix = _("Declined: ");
1261                 else if (status == ICAL_PARTSTAT_TENTATIVE)
1262                         prefix = _("Tentatively Accepted: ");
1263                 else 
1264                         prefix = "Re: "; 
1265         } else if (event->method == ICAL_METHOD_PUBLISH) {
1266                 method_str = "PUBLISH";
1267         } else if (event->method == ICAL_METHOD_CANCEL) {
1268                 method_str = "CANCEL";
1269         } else {
1270                 method_str = "REQUEST";
1271         }
1272         
1273         subject = g_strdup_printf("%s%s", prefix, event->summary);
1274
1275         conv_encode_header_full(enc_subject, sizeof(enc_subject), subject, strlen("Subject: "), 
1276                         FALSE, conv_get_outgoing_charset_str());
1277         from = is_reply?account->name:(event->orgname?event->orgname:"");
1278         conv_encode_header_full(enc_from, sizeof(enc_from), from, strlen("From: "), 
1279                         TRUE, conv_get_outgoing_charset_str());
1280
1281         if (is_pseudo_display && event->uid) {
1282                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",event->uid);
1283         } else {
1284                 calmsgid = g_strdup("");
1285         }
1286
1287         msgid = prefs_account_generate_msgid(account);
1288
1289         result = g_strdup_printf("%s"
1290                                 "From: %s <%s>\n"
1291                                 "To: <%s>\n"
1292                                 "Subject: %s\n"
1293                                 "Date: %s\n"
1294                                 "MIME-Version: 1.0\n"
1295                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"\n"
1296                                 "Content-Transfer-Encoding: 8bit\n"
1297                                 "%s"
1298                                 "%s: <%s>\n",
1299                                 queue_headers,
1300                                 enc_from,
1301                                 is_reply ? account->address:event->organizer,
1302                                 is_reply ? event->organizer:(attendees?attendees:event->organizer),
1303                                 enc_subject,
1304                                 date,
1305                                 method_str,
1306                                 CS_UTF_8,
1307                                 calmsgid,
1308                                 is_pseudo_display?
1309                                         "In-Reply-To":"Message-ID",
1310                                 is_pseudo_display?
1311                                         event_to_today_str(event, 0):msgid);
1312         g_free(calmsgid);
1313         g_free(subject);
1314         g_free(save_folder);
1315         g_free(queue_headers);
1316         g_free(attendees);
1317         g_free(msgid);
1318         return result;                  
1319                                                                                
1320
1321 }
1322
1323 static gchar *write_headers_ical(PrefsAccount   *account, 
1324                             icalcomponent       *ievent,
1325                             gchar               *orga)
1326 {
1327         gchar subject[512];
1328         gchar date[RFC822_DATE_BUFFSIZE];
1329         gchar *result = NULL;
1330         gchar *method_str = NULL;
1331         gchar *summary = NULL;
1332         gchar *organizer = NULL;
1333         gchar *orgname = NULL;
1334         icalproperty *prop = NULL;
1335         gchar *calmsgid = NULL;
1336
1337         time_t t = (time_t)0;
1338
1339         memset(subject, 0, sizeof(subject));
1340         memset(date, 0, sizeof(date));
1341         
1342         prop = icalcomponent_get_first_property(ievent, ICAL_SUMMARY_PROPERTY);
1343         if (prop) {
1344                 summary = g_strdup(icalproperty_get_summary(prop));
1345                 icalproperty_free(prop);
1346         } else {
1347                 summary = g_strdup("");
1348         }
1349         
1350         while (strchr(summary, '\n'))
1351                 *(strchr(summary, '\n')) = ' ';
1352
1353         prop = icalcomponent_get_first_property(ievent, ICAL_ORGANIZER_PROPERTY);
1354         if (prop) {
1355                 organizer = g_strdup(icalproperty_get_organizer(prop));
1356                 if (icalproperty_get_parameter_as_string(prop, "CN") != NULL)
1357                         orgname = g_strdup(icalproperty_get_parameter_as_string(prop, "CN"));
1358
1359                 icalproperty_free(prop);
1360         } else {
1361                 organizer = orga? g_strdup(orga):g_strdup("");
1362         }
1363
1364         prop = icalcomponent_get_first_property(ievent, ICAL_DTSTART_PROPERTY);
1365         if (prop) {
1366                 t = icaltime_as_timet(icalproperty_get_dtstart(prop));
1367                 get_rfc822_date_from_time_t(date, sizeof(date), t);
1368         } else {
1369                 get_rfc822_date(date, sizeof(date));
1370         }
1371
1372         conv_encode_header(subject, 511, summary, strlen("Subject: "), FALSE);
1373                         
1374         method_str = "PUBLISH";
1375
1376         prop = icalcomponent_get_first_property(ievent, ICAL_UID_PROPERTY);
1377         if (prop) {
1378                 calmsgid = g_strdup_printf("Message-ID: <%s>\n",icalproperty_get_uid(prop));
1379                 icalproperty_free(prop);
1380         } else {
1381                 calmsgid = g_strdup("");
1382         }
1383         
1384
1385         result = g_strdup_printf("From: %s <%s>\n"
1386                                 "To: <%s>\n"
1387                                 "Subject: %s%s\n"
1388                                 "Date: %s\n"
1389                                 "MIME-Version: 1.0\n"
1390                                 "Content-Type: text/calendar; method=%s; charset=\"%s\"; vcalsave=\"no\"\n"
1391                                 "Content-Transfer-Encoding: quoted-printable\n"
1392                                 "%s"
1393                                 "In-Reply-To: <%s>\n",
1394                                 orgname?orgname:"",
1395                                 !strncmp(organizer, "MAILTO:", 7) ? organizer+7 : organizer,
1396                                 account->address,
1397                                 "",
1398                                 subject,
1399                                 date,
1400                                 method_str,
1401                                 conv_get_outgoing_charset_str(),
1402                                 calmsgid,
1403                                 event_to_today_str(NULL, t));
1404         
1405         g_free(calmsgid);
1406         g_free(orgname);
1407         g_free(organizer);
1408         g_free(summary);
1409
1410         return result;                  
1411                                                                                
1412
1413 }
1414
1415 static gboolean vcal_manager_send (PrefsAccount         *account, 
1416                                      VCalEvent          *event,
1417                                      gboolean            is_reply)
1418 {
1419         gchar *tmpfile = NULL;
1420         gint msgnum;
1421         FolderItem *folderitem;
1422         gchar *msgpath = NULL;
1423         Folder *folder = NULL;
1424         
1425         tmpfile = vcal_manager_event_dump(event, is_reply, FALSE, NULL, TRUE);
1426
1427         if (!tmpfile)
1428                 return FALSE;
1429
1430         folderitem = account_get_special_folder(account, F_QUEUE);
1431         if (!folderitem) {
1432                 g_warning("can't find queue folder for %s", account->address);
1433                 g_unlink(tmpfile);
1434                 g_free(tmpfile);
1435                 return FALSE;
1436         }
1437         folder_item_scan(folderitem);
1438         
1439         if ((msgnum = folder_item_add_msg(folderitem, tmpfile, NULL, TRUE)) < 0) {
1440                 g_warning("can't queue the message");
1441                 g_unlink(tmpfile);
1442                 g_free(tmpfile);
1443                 return FALSE;
1444         }
1445
1446         msgpath = folder_item_fetch_msg(folderitem, msgnum);
1447         
1448         if (!prefs_common_get_prefs()->work_offline) {
1449                 gchar *err = NULL;
1450                 gboolean queued_removed = FALSE;
1451                 gint val = procmsg_send_message_queue_with_lock(msgpath, &err, folderitem, msgnum, &queued_removed);
1452                 if (val == 0) {
1453                         if (!queued_removed)
1454                                 folder_item_remove_msg(folderitem, msgnum);
1455                         folder_item_scan(folderitem);
1456                 } else if (err) {
1457                         alertpanel_error_log("%s", err);
1458                         g_free(err);
1459                 }
1460         }
1461         g_unlink(tmpfile);
1462         g_free(tmpfile);
1463         g_free(msgpath);
1464
1465         folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1466         if (folder)
1467                 folder_item_scan(folder->inbox);
1468
1469         vcalviewer_reload(folder->inbox);
1470         return TRUE;
1471 }
1472
1473 gboolean vcal_manager_reply (PrefsAccount       *account, 
1474                              VCalEvent          *event)
1475 {
1476         return vcal_manager_send(account, event, TRUE);
1477 }
1478
1479 gboolean vcal_manager_request (PrefsAccount     *account, 
1480                                VCalEvent        *event)
1481 {
1482         return vcal_manager_send(account, event, FALSE);
1483 }
1484
1485 EventTime event_to_today(VCalEvent *event, time_t t)
1486 {
1487         struct tm evtstart, today;
1488         time_t evtstart_t, today_t;
1489         struct icaltimetype itt;
1490
1491         tzset();
1492         
1493         today_t = time(NULL);
1494         if (event) {
1495                 itt = icaltime_from_string(event->dtstart);
1496                 evtstart_t = icaltime_as_timet(itt);
1497         } else {
1498                 evtstart_t = t;
1499         }
1500         
1501 #ifndef G_OS_WIN32
1502         struct tm buft;
1503         today = *localtime_r(&today_t, &buft);
1504         localtime_r(&evtstart_t, &evtstart);
1505 #else
1506         if (today_t < 0)
1507                 today_t = 1;
1508         if (evtstart_t < 0)
1509                 evtstart_t = 1;
1510         today = *localtime(&today_t);
1511         evtstart = *localtime(&evtstart_t);
1512 #endif
1513         
1514         if (today.tm_year == evtstart.tm_year) {
1515                 int days = evtstart.tm_yday - today.tm_yday;
1516                 if (days < 0) {
1517                         return EVENT_PAST;
1518                 } else if (days == 0) {
1519                         return EVENT_TODAY;
1520                 } else if (days == 1) {
1521                         return EVENT_TOMORROW;
1522                 } else if (days > 1 && days < 7) {
1523                         return EVENT_THISWEEK;
1524                 } else {
1525                         return EVENT_LATER;
1526                 }
1527         } else if (today.tm_year > evtstart.tm_year) {
1528                 return EVENT_PAST;
1529         } else if (today.tm_year == evtstart.tm_year - 1) {
1530                 int days = ((365 - today.tm_yday) + evtstart.tm_yday);
1531                 if (days == 0) {
1532                         return EVENT_TODAY;
1533                 } else if (days == 1) {
1534                         return EVENT_TOMORROW;
1535                 } else if (days > 1 && days < 7) {
1536                         return EVENT_THISWEEK;
1537                 } else {
1538                         return EVENT_LATER;
1539                 }
1540         } else 
1541                 return EVENT_LATER;
1542 }
1543
1544 const gchar *event_to_today_str(VCalEvent *event, time_t t)
1545 {
1546         EventTime days = event_to_today(event, t);
1547         switch(days) {
1548         case EVENT_PAST:
1549                 return EVENT_PAST_ID;
1550         case EVENT_TODAY:
1551                 return EVENT_TODAY_ID;
1552         case EVENT_TOMORROW:
1553                 return EVENT_TOMORROW_ID;
1554         case EVENT_THISWEEK:
1555                 return EVENT_THISWEEK_ID;
1556         case EVENT_LATER:
1557                 return EVENT_LATER_ID;
1558         }
1559         return NULL;
1560 }