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